From 5e3d37a6a067c63c5606a3d2bfe52f14eaeb6952 Mon Sep 17 00:00:00 2001 From: anoshenko Date: Fri, 5 Jul 2024 16:41:07 +0300 Subject: [PATCH] Added Start, Stop, Pause, and Resume methods to Animation interface --- CHANGELOG.md | 1 + animation.go | 53 +++++++++++++++-------- animationRun.go | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ canvas.go | 7 +++- session.go | 47 +++++++++++++++++---- 5 files changed, 188 insertions(+), 29 deletions(-) create mode 100644 animationRun.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 536f15c..d69d149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # v0.17.0 * Added "mod", "rem", "round", "round-up", "round-down", and "round-to-zero" SizeFunc functions * Added ModSize, RemSize, RoundSize, RoundUpSize, RoundDownSize, and RoundToZeroSize functions +* Added Start, Stop, Pause, and Resume methods to Animation interface # v0.16.0 * Can use ListAdapter as "content" property value of ListLayout diff --git a/animation.go b/animation.go index a572b39..93fc88b 100644 --- a/animation.go +++ b/animation.go @@ -121,6 +121,10 @@ type animationData struct { propertyList keyFramesName string usageCounter int + view View + listener func(view View, animation Animation, event string) + oldListeners map[string][]func(View, string) + oldAnimation []Animation } // Animation interface is used to set animation parameters. Used properties: @@ -128,6 +132,17 @@ type animationData struct { type Animation interface { Properties fmt.Stringer + + // Start starts the animation for the view specified by the first argument. + // The second argument specifies the animation event listener (can be nil) + Start(view View, listener func(view View, animation Animation, event string)) bool + // Stop stops the animation + Stop() + // Pause pauses the animation + Pause() + // Resume resumes an animation that was stopped using the Pause method + Resume() + writeTransitionString(tag string, buffer *strings.Builder) animationCSS(session Session) string transitionCSS(buffer *strings.Builder, session Session) @@ -163,19 +178,29 @@ func NewAnimation(params Params) Animation { return animation } -func (animation *animationData) hasAnimatedProperty() bool { - props := animation.getRaw(PropertyTag) - if props == nil { +func (animation *animationData) animatedProperties() []AnimatedProperty { + value := animation.getRaw(PropertyTag) + if value == nil { ErrorLog("There are no animated properties.") - return false + return nil } - if _, ok := props.([]AnimatedProperty); !ok { + props, ok := value.([]AnimatedProperty) + if !ok { ErrorLog("Invalid animated properties.") - return false + return nil } - return true + if len(props) == 0 { + ErrorLog("There are no animated properties.") + return nil + } + + return props +} + +func (animation *animationData) hasAnimatedProperty() bool { + return animation.animatedProperties() != nil } func (animation *animationData) animationName() string { @@ -394,19 +419,11 @@ func (animation *animationData) String() string { func (animation *animationData) animationCSS(session Session) string { if animation.keyFramesName == "" { - props := animation.getRaw(PropertyTag) - if props == nil { - ErrorLog("There are no animated properties.") + if props := animation.animatedProperties(); props != nil { + animation.keyFramesName = session.registerAnimation(props) + } else { return "" } - - animatedProps, ok := props.([]AnimatedProperty) - if !ok || len(animatedProps) == 0 { - ErrorLog("Invalid animated properties.") - return "" - } - - animation.keyFramesName = session.registerAnimation(animatedProps) } buffer := allocStringBuilder() diff --git a/animationRun.go b/animationRun.go new file mode 100644 index 0000000..6ebbc8a --- /dev/null +++ b/animationRun.go @@ -0,0 +1,109 @@ +package rui + +func (animation *animationData) Start(view View, listener func(view View, animation Animation, event string)) bool { + if view == nil { + ErrorLog("nil View in animation.Start() function") + return false + } + if !animation.hasAnimatedProperty() { + return false + } + + animation.view = view + animation.listener = listener + + animation.oldAnimation = nil + if value := view.Get(AnimationTag); value != nil { + if oldAnimation, ok := value.([]Animation); ok && len(oldAnimation) > 0 { + animation.oldAnimation = oldAnimation + } + } + + animation.oldListeners = map[string][]func(View, string){} + + setListeners := func(event string, listener func(View, string)) { + var listeners []func(View, string) = nil + if value := view.Get(event); value != nil { + if oldListeners, ok := value.([]func(View, string)); ok && len(oldListeners) > 0 { + listeners = oldListeners + } + } + + if listeners == nil { + view.Set(event, listener) + } else { + animation.oldListeners[event] = listeners + view.Set(event, append(listeners, listener)) + } + } + + setListeners(AnimationStartEvent, animation.onAnimationStart) + setListeners(AnimationEndEvent, animation.onAnimationEnd) + setListeners(AnimationCancelEvent, animation.onAnimationCancel) + setListeners(AnimationIterationEvent, animation.onAnimationIteration) + + view.Set(AnimationTag, animation) + return true +} + +func (animation *animationData) finish() { + if animation.view != nil { + for _, event := range []string{AnimationStartEvent, AnimationEndEvent, AnimationCancelEvent, AnimationIterationEvent} { + if listeners, ok := animation.oldListeners[event]; ok { + animation.view.Set(event, listeners) + } else { + animation.view.Remove(event) + } + } + + animation.view.Set(AnimationTag, animation.oldAnimation) + + animation.oldListeners = map[string][]func(View, string){} + animation.oldAnimation = nil + animation.view = nil + animation.listener = nil + } +} + +func (animation *animationData) Stop() { + animation.finish() +} + +func (animation *animationData) Pause() { + if animation.view != nil { + animation.view.Set(AnimationPaused, true) + } +} + +func (animation *animationData) Resume() { + if animation.view != nil { + animation.view.Remove(AnimationPaused) + } +} + +func (animation *animationData) onAnimationStart(view View, _ string) { + if animation.view != nil && animation.listener != nil { + animation.listener(animation.view, animation, AnimationStartEvent) + } +} + +func (animation *animationData) onAnimationEnd(view View, _ string) { + if animation.view != nil { + if animation.listener != nil { + animation.listener(animation.view, animation, AnimationEndEvent) + } + animation.finish() + } +} + +func (animation *animationData) onAnimationIteration(view View, _ string) { + if animation.view != nil && animation.listener != nil { + animation.listener(animation.view, animation, AnimationIterationEvent) + } +} + +func (animation *animationData) onAnimationCancel(view View, _ string) { + if animation.view != nil && animation.listener != nil { + animation.listener(animation.view, animation, AnimationCancelEvent) + } +} diff --git a/canvas.go b/canvas.go index 5895c59..0f532cd 100644 --- a/canvas.go +++ b/canvas.go @@ -299,10 +299,13 @@ type canvasData struct { } func newCanvas(view CanvasView) Canvas { + session := view.Session() + if !session.canvasStart(view.htmlID()) { + return nil + } canvas := new(canvasData) canvas.view = view - canvas.session = view.Session() - canvas.session.canvasStart(view.htmlID()) + canvas.session = session return canvas } diff --git a/session.go b/session.go index b9eab0c..5af9cb0 100644 --- a/session.go +++ b/session.go @@ -144,7 +144,9 @@ type Session interface { sendResponse() addAnimationCSS(css string) removeAnimation(keyframe string) - canvasStart(htmlID string) + htmlPropertyValue(htmlID, name string) string + + canvasStart(htmlID string) bool callCanvasFunc(funcName string, args ...any) createCanvasVar(funcName string, args ...any) any callCanvasVarFunc(v any, funcName string, args ...any) @@ -152,7 +154,7 @@ type Session interface { updateCanvasProperty(property string, value any) canvasFinish() canvasTextMetrics(htmlID, font, text string) TextMetrics - htmlPropertyValue(htmlID, name string) string + addToEventsQueue(data DataObject) handleAnswer(command string, data DataObject) bool handleRootSize(data DataObject) @@ -427,20 +429,32 @@ func (session *sessionData) appendToInnerHTML(htmlID, html string) { } func (session *sessionData) updateCSSProperty(htmlID, property, value string) { - if !session.ignoreViewUpdates() && session.bridge != nil { - session.bridge.updateCSSProperty(htmlID, property, value) + if !session.ignoreViewUpdates() { + if session.bridge != nil { + session.bridge.updateCSSProperty(htmlID, property, value) + } else { + ErrorLog("No connection") + } } } func (session *sessionData) updateProperty(htmlID, property string, value any) { - if !session.ignoreViewUpdates() && session.bridge != nil { - session.bridge.updateProperty(htmlID, property, value) + if !session.ignoreViewUpdates() { + if session.bridge != nil { + session.bridge.updateProperty(htmlID, property, value) + } else { + ErrorLog("No connection") + } } } func (session *sessionData) removeProperty(htmlID, property string) { - if !session.ignoreViewUpdates() && session.bridge != nil { - session.bridge.removeProperty(htmlID, property) + if !session.ignoreViewUpdates() { + if session.bridge != nil { + session.bridge.removeProperty(htmlID, property) + } else { + ErrorLog("No connection") + } } } @@ -448,18 +462,23 @@ func (session *sessionData) startUpdateScript(htmlID string) bool { if session.bridge != nil { return session.bridge.startUpdateScript(htmlID) } + ErrorLog("No connection") return false } func (session *sessionData) finishUpdateScript(htmlID string) { if session.bridge != nil { session.bridge.finishUpdateScript(htmlID) + } else { + ErrorLog("No connection") } } func (session *sessionData) sendResponse() { if session.bridge != nil { session.bridge.sendResponse() + } else { + ErrorLog("No connection") } } @@ -467,6 +486,8 @@ func (session *sessionData) addAnimationCSS(css string) { session.animationCSS += css if session.bridge != nil { session.bridge.appendAnimationCSS(css) + } else { + ErrorLog("No connection") } } @@ -503,14 +524,20 @@ func (session *sessionData) removeAnimation(keyframe string) { session.animationCSS = css[:index] + css[end:] if session.bridge != nil { session.bridge.setAnimationCSS(session.animationCSS) + } else { + ErrorLog("No connection") } } } -func (session *sessionData) canvasStart(htmlID string) { +func (session *sessionData) canvasStart(htmlID string) bool { if session.bridge != nil { session.bridge.canvasStart(htmlID) + return true } + + ErrorLog("No connection") + return false } func (session *sessionData) callCanvasFunc(funcName string, args ...any) { @@ -587,6 +614,8 @@ func (session *sessionData) handleAnswer(command string, data DataObject) bool { if session.bridge != nil { session.bridge.sendResponse() + } else { + ErrorLog("No connection") } return true }