From 2f3de8fce3d2b2a8b70116ad739e25e9249bfb23 Mon Sep 17 00:00:00 2001 From: anoshenko Date: Mon, 1 Jul 2024 19:17:03 +0300 Subject: [PATCH] Optimised animation --- animation.go | 22 +++++++++++++++++----- cssBuilder.go | 26 ++++++++++++++------------ session.go | 40 ++++++++++++++++++++++++++++++++++++---- theme.go | 2 +- view.go | 19 +++++++++++++++++++ viewStyleSet.go | 16 ++++++++++++---- webBridge.go | 2 -- 7 files changed, 99 insertions(+), 28 deletions(-) diff --git a/animation.go b/animation.go index f2cbd66..a572b39 100644 --- a/animation.go +++ b/animation.go @@ -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 } diff --git a/cssBuilder.go b/cssBuilder.go index 852c30d..52a4824 100644 --- a/cssBuilder.go +++ b/cssBuilder.go @@ -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`) diff --git a/session.go b/session.go index 15c7f90..b9eab0c 100644 --- a/session.go +++ b/session.go @@ -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() { - if session.bridge != nil { - session.bridge.setAnimationCSS("") +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.animationCSS) + } } } diff --git a/theme.go b/theme.go index fee0f16..009e4b1 100644 --- a/theme.go +++ b/theme.go @@ -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{} diff --git a/view.go b/view.go index 287f3a6..b1ca488 100644 --- a/view.go +++ b/view.go @@ -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 diff --git a/viewStyleSet.go b/viewStyleSet.go index 3ce90ac..e5b569a 100644 --- a/viewStyleSet.go +++ b/viewStyleSet.go @@ -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 } diff --git a/webBridge.go b/webBridge.go index df44857..4192951 100644 --- a/webBridge.go +++ b/webBridge.go @@ -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) {