Optimised animation

This commit is contained in:
anoshenko 2024-07-01 19:17:03 +03:00
parent 09031b9fa0
commit 2f3de8fce3
7 changed files with 99 additions and 28 deletions

View File

@ -120,6 +120,7 @@ type AnimatedProperty struct {
type animationData struct {
propertyList
keyFramesName string
usageCounter int
}
// Animation interface is used to set animation parameters. Used properties:
@ -132,6 +133,8 @@ type Animation interface {
transitionCSS(buffer *strings.Builder, session Session)
hasAnimatedProperty() bool
animationName() string
used()
unused(session Session)
}
func parseAnimation(obj DataObject) Animation {
@ -179,6 +182,17 @@ func (animation *animationData) animationName() string {
return animation.keyFramesName
}
func (animation *animationData) used() {
animation.usageCounter++
}
func (animation *animationData) unused(session Session) {
animation.usageCounter--
if animation.usageCounter <= 0 && animation.keyFramesName != "" {
session.removeAnimation(animation.keyFramesName)
}
}
func (animation *animationData) normalizeTag(tag string) string {
tag = strings.ToLower(tag)
if tag == Direction {
@ -387,7 +401,7 @@ func (animation *animationData) animationCSS(session Session) string {
}
animatedProps, ok := props.([]AnimatedProperty)
if !ok {
if !ok || len(animatedProps) == 0 {
ErrorLog("Invalid animated properties.")
return ""
}
@ -549,6 +563,7 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
var cssBuilder cssStyleBuilder
cssBuilder.init(0)
cssBuilder.startAnimation(name)
fromParams := Params{}
@ -606,10 +621,7 @@ func (session *sessionData) registerAnimation(props []AnimatedProperty) string {
cssBuilder.endAnimationFrame()
cssBuilder.endAnimation()
style := cssBuilder.finish()
session.animationCSS += style
session.addAnimationCSS(style)
session.addAnimationCSS(cssBuilder.finish())
return name
}

View File

@ -150,9 +150,11 @@ func (builder *cssValueBuilder) addValues(key, separator string, values ...strin
}
}
func (builder *cssStyleBuilder) init() {
func (builder *cssStyleBuilder) init(kbSize int) {
builder.buffer = allocStringBuilder()
builder.buffer.Grow(16 * 1024)
if kbSize > 0 {
builder.buffer.Grow(kbSize * 1024)
}
}
func (builder *cssStyleBuilder) finish() string {
@ -168,7 +170,7 @@ func (builder *cssStyleBuilder) finish() string {
func (builder *cssStyleBuilder) startMedia(rule string) {
if builder.buffer == nil {
builder.init()
builder.init(0)
}
builder.buffer.WriteString(`@media screen`)
builder.buffer.WriteString(rule)
@ -178,7 +180,7 @@ func (builder *cssStyleBuilder) startMedia(rule string) {
func (builder *cssStyleBuilder) endMedia() {
if builder.buffer == nil {
builder.init()
builder.init(0)
}
builder.buffer.WriteString(`}\n`)
builder.media = false
@ -192,7 +194,7 @@ func (builder *cssStyleBuilder) startStyle(name string) {
}
if builder.buffer == nil {
builder.init()
builder.init(0)
}
if builder.media {
builder.buffer.WriteString(`\t`)
@ -210,7 +212,7 @@ func (builder *cssStyleBuilder) startStyle(name string) {
func (builder *cssStyleBuilder) endStyle() {
if builder.buffer == nil {
builder.init()
builder.init(0)
}
if builder.media {
builder.buffer.WriteString(`\t`)
@ -220,7 +222,7 @@ func (builder *cssStyleBuilder) endStyle() {
func (builder *cssStyleBuilder) startAnimation(name string) {
if builder.buffer == nil {
builder.init()
builder.init(0)
}
builder.media = true
@ -231,7 +233,7 @@ func (builder *cssStyleBuilder) startAnimation(name string) {
func (builder *cssStyleBuilder) endAnimation() {
if builder.buffer == nil {
builder.init()
builder.init(0)
}
builder.buffer.WriteString(`}\n`)
builder.media = false
@ -239,7 +241,7 @@ func (builder *cssStyleBuilder) endAnimation() {
func (builder *cssStyleBuilder) startAnimationFrame(name string) {
if builder.buffer == nil {
builder.init()
builder.init(0)
}
builder.buffer.WriteString(`\t`)
@ -249,7 +251,7 @@ func (builder *cssStyleBuilder) startAnimationFrame(name string) {
func (builder *cssStyleBuilder) endAnimationFrame() {
if builder.buffer == nil {
builder.init()
builder.init(0)
}
builder.buffer.WriteString(`\t}\n`)
}
@ -257,7 +259,7 @@ func (builder *cssStyleBuilder) endAnimationFrame() {
func (builder *cssStyleBuilder) add(key, value string) {
if value != "" {
if builder.buffer == nil {
builder.init()
builder.init(0)
}
if builder.media {
builder.buffer.WriteString(`\t`)
@ -276,7 +278,7 @@ func (builder *cssStyleBuilder) addValues(key, separator string, values ...strin
}
if builder.buffer == nil {
builder.init()
builder.init(0)
}
if builder.media {
builder.buffer.WriteString(`\t`)

View File

@ -143,7 +143,7 @@ type Session interface {
finishUpdateScript(htmlID string)
sendResponse()
addAnimationCSS(css string)
clearAnimation()
removeAnimation(keyframe string)
canvasStart(htmlID string)
callCanvasFunc(funcName string, args ...any)
createCanvasVar(funcName string, args ...any) any
@ -464,14 +464,46 @@ func (session *sessionData) sendResponse() {
}
func (session *sessionData) addAnimationCSS(css string) {
session.animationCSS += css
if session.bridge != nil {
session.bridge.appendAnimationCSS(css)
}
}
func (session *sessionData) clearAnimation() {
func (session *sessionData) removeAnimation(keyframe string) {
css := session.animationCSS
index := strings.Index(css, "@keyframes "+keyframe)
if index < 0 {
return
}
start := strings.IndexRune(css[index:], '{')
if start < 0 {
return
}
n := 1
end := -1
for i := start + index + 1; i < len(css); i++ {
if css[i] == '}' {
n--
if n == 0 {
end = i + 1
if end < len(css) && css[end] == '\n' {
end++
}
break
}
} else if css[i] == '{' {
n++
}
}
if end > index {
session.animationCSS = css[:index] + css[end:]
if session.bridge != nil {
session.bridge.setAnimationCSS("")
session.bridge.setAnimationCSS(session.animationCSS)
}
}
}

View File

@ -589,7 +589,7 @@ func (theme *theme) cssText(session Session) string {
}
var builder cssStyleBuilder
builder.init()
builder.init(16)
styleList := func(styles map[string]ViewStyle) []string {
ruiStyles := []string{}

19
view.go
View File

@ -341,6 +341,25 @@ func (view *viewData) set(tag string, value any) bool {
}
view.viewID = text
case AnimationTag:
oldAnimations := []Animation{}
if val, ok := view.properties[AnimationTag]; ok && val != nil {
if animation, ok := val.([]Animation); ok {
oldAnimations = animation
}
}
if !view.setAnimation(tag, value) {
return false
}
for _, animation := range oldAnimations {
animation.unused(view.session)
}
if view.created {
viewPropertyChanged(view, tag)
}
case TabIndex, "tab-index":
if !view.setIntProperty(tag, value) {
return false

View File

@ -143,18 +143,26 @@ func (style *viewStyle) setTransition(tag string, value any) bool {
}
func (style *viewStyle) setAnimation(tag string, value any) bool {
set := func(animations []Animation) {
style.properties[tag] = animations
for _, animation := range animations {
animation.used()
}
}
switch value := value.(type) {
case Animation:
style.properties[tag] = []Animation{value}
set([]Animation{value})
return true
case []Animation:
style.properties[tag] = value
set(value)
return true
case DataObject:
if animation := parseAnimation(value); animation.hasAnimatedProperty() {
style.properties[tag] = []Animation{animation}
set([]Animation{animation})
return true
}
@ -174,7 +182,7 @@ func (style *viewStyle) setAnimation(tag string, value any) bool {
}
}
if result && len(animations) > 0 {
style.properties[tag] = animations
set(animations)
}
return result
}

View File

@ -284,7 +284,6 @@ func (bridge *webBridge) removeProperty(htmlID, property string) {
}
func (bridge *webBridge) appendAnimationCSS(css string) {
//bridge.callFunc("appendAnimationCSS", css)
bridge.writeMessage(`{
let styles = document.getElementById('ruiAnimations');
if (styles) {
@ -294,7 +293,6 @@ func (bridge *webBridge) appendAnimationCSS(css string) {
}
func (bridge *webBridge) setAnimationCSS(css string) {
//bridge.callFunc("setAnimationCSS", css)
bridge.writeMessage(`{
let styles = document.getElementById('ruiAnimations');
if (styles) {