[feature request]: Record/replay css animation's `currentTime`
oceanMa1 opened this issue · comments
Preflight Checklist
- I have searched the issue tracker for a bug report that matches the one I want to file, without success.
What package is this bug report for?
rrweb
Version
2.0.0-alpha.13
Expected Behavior
When I use checkoutEveryNms: 2000 for recording, if the content being recorded contains animation, then every 2s this animation will be triggered repeatedly during playback. However, it works normally if checkoutEveryNms are not used.
Actual Behavior
When I use checkoutEveryNms: 2000 for recording, if the content being recorded contains animation, then every 2s this animation will be triggered repeatedly during playback. However, it works normally if checkoutEveryNms are not used.
Steps to Reproduce
<template>
<div id="container" style="display: flex">
<div
id="container2"
style="border: 1px solid; width: 800px; height: 600px"
></div>
<div
v-if="showTable"
class="animate__animated animate__fadeInUp"
>animate__fadeInUp
animate__fadeInUp
animate__fadeInUp</div>
<div>
<button @click="showTable=true">Show</button>
<button @click="rePlay">Replay</button>
</div>
</div>
</template>
<script setup>
import * as rrweb from 'rrweb';
import rrwebPlayer from 'rrweb-player';
import 'rrweb-player/dist/style.css';
import { onMounted, ref } from 'vue';
import 'animate.css';
const showTable=ref(false);
let events = [];
onMounted(() => {
_initRecorder();
});
const _initRecorder = () => {
rrweb.record({
checkoutEveryNms: 2000,
emit(event) {
events.push(event);
},
});
}
const rePlay = () => {
const replayer =new rrwebPlayer({
target: document.getElementById('container2'),
props: {
events,
},
});
};
</script>
Testcase Gist URL
Additional Information
No response
You've found a tricky one @oceanMa1, to get this working we'd need to call document.getAnimations()
and register the currentTime
of each animation. And then of course we'd also have set these currentTime
on animation replay as well.
Also we can listen for animationstart
events on window/document which will let us know when an animation was started: https://developer.mozilla.org/en-US/docs/Web/API/Element/animationstart_event same for animationend
This is very much related to #1143
Is the problem caused because the checkouteverynth approach creates a FullSnapshot event which causes an entire rebuild of the replayer state?
If that's the case, I wonder would some sort of diff application via rrdom be the right thing to do instead, whereby an animation would naturally avoid being retriggered if it's dom hasn't changed?
I think you're correct. When I replay and filter out all the snapshots after the first one, this issue can be resolved. The code is as follows. @Juice10
const rePlay = () => {
const filtered =[]
let firstSnapshotFind =false
events.forEach((item)=>{
if (!firstSnapshotFind){
filtered.push(item)
}else if (item.type!=2&&item.type!=4){//Filter the snapshot behind
filtered.push(item)
}
if (!firstSnapshotFind&&item.type==2){
firstSnapshotFind=true
}
})
const replayer =new rrwebPlayer({
target: document.getElementById('container2'),
props: {
events:filtered,
},
});
};
Is the problem caused because the checkouteverynth approach creates a FullSnapshot event which causes an entire rebuild of the replayer state?
I think that is exactly what is happening, entire rebuild is triggering the animation every time it's rebuilt.
If that's the case, I wonder would some sort of diff application via rrdom be the right thing to do instead, whereby an animation would naturally avoid being retriggered if its dom hasn't changed?
That would also solve this issue in this instance I think. And it could really help for performance by eliminating repaints.
We don't use checkouteverynth
and I don't understand why that is a preferred approach as it must massively increase the size of recordings.
And it could really help for performance by eliminating repaints.
Yes; although you probably want a full refresh for a regular FullSnapshot (as probably calculating the diff would be more work than replacing and e.g you might actually want to do the full refresh to restart animations)
Would using some of the other approaches instead of checkouteverynth
solve this for you or is that not an option?