rrweb-io / rrweb

record and replay the web

Home Page:https://www.rrweb.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[feature request]: Record/replay css animation's `currentTime`

oceanMa1 opened this issue · comments

commented

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

https://rrwebdebug.com/play/index.html?url=https%3A%2F%2Fgist.github.com%2Fmhy17376555322%2F775375301a91079cc13a0e06dc44eafe&version=2.0.0-alpha.13&virtual-dom=on&play=on

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?

commented

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?