Repository navigation
feat(layers): move, scale, turn and fade layers between keyframes - #884
Merged
Merged
Conversation
Layer.keyframes place a layer at points in time; between them it moves along the earlier keyframe's curve and can play loop effects. Video editors that animate overlays over time need the preview to follow the same keyframes their export renders. A transform gesture on a keyframed layer changes the keyframe at the playback position. Layer.opacity applies to every layer type and is baked into captures. replaceLayer takes skipUpdateHistory so a host can preview a layer without a history step, and captures grow keyframed layers so they stay sharp at their largest keyframed size.
2 of 7 tasks
Review fixes for the layer keyframes: - A crop or image rotation keeps the keyframes' rotation instead of resetting it to the layer's rotation from before the crop. - A duplicated layer moves its keyframes with it, and addLayer's zoom and padding corrections apply to the keyframes too. - A gesture, scroll or key that leaves a keyframed layer where it was adds no keyframe, which would change its easing. - Drag selection and alignment guides measure keyframed layers where they are drawn at the playback position. - A layer flipped in place turns its keyframed rotation the right way at once. - The paint layer sheet hides its opacity slider for a keyframed layer, whose keyframes set the opacity. - Layer opacity applies inside the hero, so it holds during sub-editor transitions, and a fully transparent layer lets touches through. - The paint editor scales the keyframes of the layers it hands back. - replaceLayer documents how to record previewed values as one undo step.
hm21
marked this pull request as ready for review
October 7, 2026 10:06
hm21
added a commit
to divinevideo/divine-mobile
that referenced
this pull request
Oct 7, 2026
…29.0 The keyframe editing and export need the layer keyframes, keyframe effects and windowed loops these releases add (hm21/pro_image_editor#884, hm21/pro_video_editor#235).
3 of 7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Problem. A video editor built on this package could animate a layer only in, out, or in a loop over its whole time range. It could not move a layer along a path, or grow, turn or fade it at moments the user picks, and the preview had nothing to match an export that does.
Changes.
Layer.keyframes(LayerKeyframe: time from the layer's start, offset, scale, rotation, opacity, curve) place a layer at points in time. Before the first keyframe it holds the first one, after the last one the last, and between two it moves along the earlier one's curve; rotation turns the whole difference. Helpers onLayer:keyframePlacementAt,keyframeIndexAt,setKeyframeAt,removeKeyframeAt,applyKeyframePlacement,transformKeyframes. Keyframes are serialized (minified keykf), copied byLayerCopyManagerand compared in==.LayerKeyframe.effects: loop animations such as a wiggle, played from one keyframe to the next. Each cycle is fitted so a whole number of them fills the stretch and the layer rests on both keyframes.Layer.keyframeEffectsplaces them on the video's timeline (LayerKeyframeEffectwith start, end and cycle count), so an exporter can keep the same cycles.LayerTimelineVisibilityapplies the keyframed placement as the difference to the laid-out one, turned the other way for a layer mirrored on one axis, then composes the animations and the keyframe effects on top, aspro_video_editordoes.LayerTimelineConfigs.keyframeTolerancesets how close counts as on a keyframe. Crops, flips, canvas resizes and image-frame moves transform the keyframes with the layer.setLayerTimelinetakeskeyframes.Layer.opacityfor every layer type (moved up fromPaintLayer): applied by the layer widget and baked intocaptureAsPng. A keyframed layer carries its opacity in its keyframes.replaceLayer(skipUpdateHistory: true)shows a layer without a history step, so a host can preview every value of a slider and record one step at the end.captureAsPngcaptures a keyframed layer at the largest size its keyframes grow it to (Layer.keyframeCaptureGrowth, at most 4x).Why. The divine video editor adds keyframes to overlay layers (divinevideo/divine-mobile#9845) and exports them through
pro_video_editor2.29.0 (hm21/pro_video_editor#235). This preview mirrors that renderer's keyframe and loop-window semantics, so what the user sets up is what the export shows.Tests. Keyframe model, serialization and import/export, keyframe effects and their cycle fitting, the preview (placement, mirrored layers, legacy fade, effects only between their keyframes), gesture editing, copies, crop/flip/resize transforms and capture growth. Editing and preview were checked in the divine app on a Samsung Galaxy S26 (SM-S942B).
Related Issue: none in this repository; needed for divinevideo/divine-mobile#9845.
Type of Change