add player controller
This commit is contained in:
@@ -1,6 +1,3 @@
|
|||||||
<h1>
|
|
||||||
rrweb player playground
|
|
||||||
</h1>
|
|
||||||
<Player {events} />
|
<Player {events} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
108
src/Controller.html
Normal file
108
src/Controller.html
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<div class="rr-controller">
|
||||||
|
<div class="rr-timeline">
|
||||||
|
<span class="rr-timeline__time">{formatTime(currentTime)}</span>
|
||||||
|
<div class="rr-progress">
|
||||||
|
<div class="rr-progress__step" ref:step style="width: {percentage}"></div>
|
||||||
|
<div class="rr-progress__handler" ref:handler style="left: {percentage}"></div>
|
||||||
|
</div>
|
||||||
|
<span class="rr-timeline__time">{formatTime(meta.totalTime)}</span>
|
||||||
|
</div>
|
||||||
|
<button>pause</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { formatTime } from './utils.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentTime: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
meta({ replayer }) {
|
||||||
|
return replayer.getMetaData();
|
||||||
|
},
|
||||||
|
percentage({ currentTime, meta }) {
|
||||||
|
return `${100 * currentTime / meta.totalTime}%`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
helpers: {
|
||||||
|
formatTime,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loopTimer() {
|
||||||
|
const now = performance.now();
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
function update(step) {
|
||||||
|
let { currentTime, meta } = self.get();
|
||||||
|
currentTime = Math.floor(step - now);
|
||||||
|
self.set({ currentTime });
|
||||||
|
|
||||||
|
if (currentTime < meta.totalTime) {
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onupdate({ changed, current }) {
|
||||||
|
if (changed.replayer) {
|
||||||
|
this.loopTimer();
|
||||||
|
current.replayer.play();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.rr-controller {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
background: rgba(0, 0, 0, .5);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rr-timeline {
|
||||||
|
width: 80%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.rr-timeline__time {
|
||||||
|
padding: 0 20px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rr-progress {
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background: white;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rr-progress__step {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
background: orange
|
||||||
|
}
|
||||||
|
|
||||||
|
.rr-progress__handler {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: orange;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
112
src/Player.html
112
src/Player.html
@@ -1,10 +1,65 @@
|
|||||||
<div class="rr-player">
|
<div class="rr-player">
|
||||||
<div class="rr-player__frame" ref:frame { style }></div>
|
<div class="rr-player__frame" ref:frame { style }></div>
|
||||||
|
{#if replayer}
|
||||||
|
<Controller { replayer } />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Replayer } from 'rrweb';
|
||||||
|
import { inlineCss } from './utils.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Controller: './Controller.html',
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
width: 1024,
|
||||||
|
height: 576,
|
||||||
|
events: [],
|
||||||
|
replayer: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
style({ width, height }) {
|
||||||
|
return inlineCss({
|
||||||
|
width: `${width}px`,
|
||||||
|
height: `${height}px`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateScale(el, frameDimension) {
|
||||||
|
const { width, height } = this.get();
|
||||||
|
const widthScale = (width - 20) / frameDimension.width;
|
||||||
|
const heightScale = (height - 20) / frameDimension.height;
|
||||||
|
el.style.transform =
|
||||||
|
`scale(${Math.min(widthScale, heightScale, 1)})` +
|
||||||
|
'translate(-50%, -50%)';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
oncreate(p) {
|
||||||
|
const { events } = this.get();
|
||||||
|
const replayer = new Replayer(events, {
|
||||||
|
speed: 1,
|
||||||
|
root: this.refs.frame,
|
||||||
|
});
|
||||||
|
replayer.on('resize', (dimension) =>
|
||||||
|
this.updateScale(replayer.wrapper, dimension)
|
||||||
|
);
|
||||||
|
this.set({
|
||||||
|
replayer,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.rr-player {
|
.rr-player {
|
||||||
border: 3px solid indianred;
|
background: white;
|
||||||
float: left;
|
float: left;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
@@ -17,9 +72,14 @@
|
|||||||
float: left;
|
float: left;
|
||||||
clear: both;
|
clear: both;
|
||||||
transform-origin: top left;
|
transform-origin: top left;
|
||||||
left: 50%;
|
left: calc(50% - 10px);
|
||||||
top: 50%;
|
top: calc(50% - 10px);
|
||||||
border: 2px solid grey;
|
margin: 10px;
|
||||||
|
box-shadow: 0 3px 28px rgba(0, 0, 0, 0.25), 0 1px 10px rgba(0, 0, 0, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.replayer-wrapper > iframe) {
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get from rrweb */
|
/* get from rrweb */
|
||||||
@@ -44,47 +104,3 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
|
||||||
import { Replayer } from 'rrweb';
|
|
||||||
import { inlineCss } from './util.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
width: 1024,
|
|
||||||
height: 576,
|
|
||||||
events: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
style({ width, height }) {
|
|
||||||
return inlineCss({
|
|
||||||
width: `${width}px`,
|
|
||||||
height: `${height}px`,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
updateScale(el, frameDimension) {
|
|
||||||
const { width, height } = this.get();
|
|
||||||
const widthScale = width / frameDimension.width;
|
|
||||||
const heightScale = height / frameDimension.height;
|
|
||||||
el.style.transform =
|
|
||||||
`scale(${Math.min(widthScale, heightScale, 1)})` +
|
|
||||||
'translate(-50%, -50%)';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
oncreate(p) {
|
|
||||||
const { events } = this.get();
|
|
||||||
const replayer = new Replayer(events, {
|
|
||||||
speed: 1,
|
|
||||||
root: this.refs.frame,
|
|
||||||
onResize: (dimension) => this.updateScale(replayer.wrapper, dimension),
|
|
||||||
});
|
|
||||||
replayer.play();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export function inlineCss(cssObj) {
|
|
||||||
let style = '';
|
|
||||||
Object.keys(cssObj).forEach(key => {
|
|
||||||
style += `${key}: ${cssObj[key]};`;
|
|
||||||
});
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
33
src/utils.js
Normal file
33
src/utils.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
export function inlineCss(cssObj) {
|
||||||
|
let style = '';
|
||||||
|
Object.keys(cssObj).forEach(key => {
|
||||||
|
style += `${key}: ${cssObj[key]};`;
|
||||||
|
});
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
function padZero(num, len = 2) {
|
||||||
|
const threshold = Math.pow(10, len - 1);
|
||||||
|
if (num < threshold) {
|
||||||
|
num = String(num);
|
||||||
|
while (String(threshold).length > num.length) {
|
||||||
|
num = '0' + num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SECOND = 1000;
|
||||||
|
const MINUTE = 60 * SECOND;
|
||||||
|
const HOUR = 60 * MINUTE;
|
||||||
|
export function formatTime(ms) {
|
||||||
|
const hour = Math.floor(ms / HOUR);
|
||||||
|
ms = ms % HOUR;
|
||||||
|
const minute = Math.floor(ms / MINUTE);
|
||||||
|
ms = ms % MINUTE;
|
||||||
|
const second = Math.floor(ms / SECOND);
|
||||||
|
if (hour) {
|
||||||
|
return `${padZero(hour)}:${padZero(minute)}:${padZero(minute)}`;
|
||||||
|
}
|
||||||
|
return `${padZero(hour)}:${padZero(second)}`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user