325 lines
9.0 KiB
HTML
325 lines
9.0 KiB
HTML
{#if showController}
|
|
<div class="rr-controller">
|
|
<div class="rr-timeline">
|
|
<span class="rr-timeline__time">{formatTime(currentTime)}</span>
|
|
<div
|
|
class="rr-progress"
|
|
class:disabled="isSkipping"
|
|
ref:progress on:click="handleProgressClick(event)"
|
|
>
|
|
<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>
|
|
<div class="rr-controller__btns">
|
|
<button on:click="toggle()">
|
|
{#if isPlaying}
|
|
<svg
|
|
class="icon"
|
|
viewBox="0 0 1024 1024"
|
|
version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
width="16"
|
|
height="16"
|
|
>
|
|
<path
|
|
d="M682.65984 128q53.00224 0 90.50112 37.49888t37.49888 90.50112l0 512q0 53.00224-37.49888 90.50112t-90.50112 37.49888-90.50112-37.49888-37.49888-90.50112l0-512q0-53.00224 37.49888-90.50112t90.50112-37.49888zM341.34016 128q53.00224 0 90.50112 37.49888t37.49888 90.50112l0 512q0 53.00224-37.49888 90.50112t-90.50112 37.49888-90.50112-37.49888-37.49888-90.50112l0-512q0-53.00224 37.49888-90.50112t90.50112-37.49888zM341.34016 213.34016q-17.67424 0-30.16704 12.4928t-12.4928 30.16704l0 512q0 17.67424 12.4928 30.16704t30.16704 12.4928 30.16704-12.4928 12.4928-30.16704l0-512q0-17.67424-12.4928-30.16704t-30.16704-12.4928zM682.65984 213.34016q-17.67424 0-30.16704 12.4928t-12.4928 30.16704l0 512q0 17.67424 12.4928 30.16704t30.16704 12.4928 30.16704-12.4928 12.4928-30.16704l0-512q0-17.67424-12.4928-30.16704t-30.16704-12.4928z"
|
|
></path>
|
|
</svg>
|
|
{:else}
|
|
<svg
|
|
class="icon"
|
|
viewBox="0 0 1024 1024"
|
|
version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
width="16"
|
|
height="16"
|
|
>
|
|
<path
|
|
d="M170.65984 896l0-768 640 384zM644.66944 512l-388.66944-233.32864 0 466.65728z"
|
|
></path>
|
|
</svg>
|
|
{/if}
|
|
</button>
|
|
{#each [1, 2, 4, 8] as s}
|
|
<button
|
|
class:active="s === speed && !isSkipping"
|
|
on:click="setSpeed(s)"
|
|
disabled="{isSkipping}"
|
|
>
|
|
{s}x
|
|
</button>
|
|
{/each}
|
|
<Switch id="skip" bind:checked="skipInactive" disabled="{isSkipping}" label="skip inactive" />
|
|
<button on:click="fire('fullscreen')">
|
|
<svg
|
|
class="icon"
|
|
viewBox="0 0 1024 1024"
|
|
version="1.1"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
width="16" height="16"
|
|
>
|
|
<defs><style type="text/css"></style></defs>
|
|
<path d="M916 380c-26.4 0-48-21.6-48-48L868 223.2 613.6 477.6c-18.4 18.4-48.8 18.4-68 0-18.4-18.4-18.4-48.8 0-68L800 156 692 156c-26.4 0-48-21.6-48-48 0-26.4 21.6-48 48-48l224 0c26.4 0 48 21.6 48 48l0 224C964 358.4 942.4 380 916 380zM231.2 860l108.8 0c26.4 0 48 21.6 48 48s-21.6 48-48 48l-224 0c-26.4 0-48-21.6-48-48l0-224c0-26.4 21.6-48 48-48 26.4 0 48 21.6 48 48L164 792l253.6-253.6c18.4-18.4 48.8-18.4 68 0 18.4 18.4 18.4 48.8 0 68L231.2 860z" p-id="1286"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<script>
|
|
import { formatTime } from './utils.js';
|
|
|
|
export default {
|
|
components: {
|
|
Switch: './components/Switch.html',
|
|
},
|
|
data() {
|
|
return {
|
|
currentTime: 0,
|
|
isPlaying: false,
|
|
isSkipping: false,
|
|
skipInactive: true,
|
|
speed: 1,
|
|
};
|
|
},
|
|
computed: {
|
|
meta({ replayer }) {
|
|
return replayer.getMetaData();
|
|
},
|
|
percentage({ currentTime, meta }) {
|
|
const percent = Math.min(1, currentTime / meta.totalTime);
|
|
return `${100 * percent}%`;
|
|
},
|
|
},
|
|
helpers: {
|
|
formatTime,
|
|
},
|
|
methods: {
|
|
loopTimer() {
|
|
const self = this;
|
|
|
|
function update() {
|
|
const { meta, isPlaying, replayer } = self.get();
|
|
if (!isPlaying) {
|
|
self.timer = null;
|
|
return;
|
|
}
|
|
|
|
const currentTime =
|
|
replayer.timer.timeOffset + replayer.getTimeOffset();
|
|
self.set({
|
|
currentTime,
|
|
});
|
|
|
|
if (currentTime < meta.totalTime) {
|
|
requestAnimationFrame(update);
|
|
}
|
|
}
|
|
|
|
this.timer = requestAnimationFrame(update);
|
|
},
|
|
play() {
|
|
const { replayer, currentTime } = this.get();
|
|
if (currentTime > 0) {
|
|
replayer.resume(currentTime);
|
|
} else {
|
|
this.set({ isPlaying: true });
|
|
replayer.play(currentTime);
|
|
}
|
|
},
|
|
pause() {
|
|
const { replayer } = this.get();
|
|
replayer.pause();
|
|
},
|
|
toggle() {
|
|
const { isPlaying } = this.get();
|
|
if (isPlaying) {
|
|
this.pause();
|
|
} else {
|
|
this.play();
|
|
}
|
|
},
|
|
setSpeed(speed) {
|
|
const { replayer, currentTime, isPlaying } = this.get();
|
|
// freeze before set speed, and resume if is playing before freeze
|
|
replayer.pause();
|
|
replayer.setConfig({ speed });
|
|
this.set({ speed });
|
|
if (isPlaying) {
|
|
replayer.resume(currentTime);
|
|
}
|
|
},
|
|
handleProgressClick(event) {
|
|
const { meta, replayer, isPlaying, isSkipping } = this.get();
|
|
if (isSkipping) {
|
|
return;
|
|
}
|
|
const progressRect = this.refs.progress.getBoundingClientRect();
|
|
const x = event.clientX - progressRect.left;
|
|
let percent = x / progressRect.width;
|
|
if (percent < 0) {
|
|
percent = 0;
|
|
} else if (percent > 1) {
|
|
percent = 1;
|
|
}
|
|
const timeOffset = meta.totalTime * percent;
|
|
this.set({ currentTime: timeOffset });
|
|
replayer.play(timeOffset);
|
|
if (!isPlaying) {
|
|
replayer.pause();
|
|
}
|
|
},
|
|
},
|
|
onupdate({ changed, current, previous }) {
|
|
if (current.replayer && !previous) {
|
|
window.replayer = current.replayer;
|
|
setTimeout(() => {
|
|
this.set({ isPlaying: true });
|
|
}, 0);
|
|
current.replayer.play(0);
|
|
if (!current.autoPlay) {
|
|
let firstFullSnapshotRebuilded = false;
|
|
current.replayer.on('fullsnapshot-rebuilded', () => {
|
|
if (!firstFullSnapshotRebuilded) {
|
|
firstFullSnapshotRebuilded = true;
|
|
current.replayer.pause();
|
|
}
|
|
});
|
|
}
|
|
current.replayer.on('pause', () => {
|
|
this.set({ isPlaying: false });
|
|
});
|
|
current.replayer.on('resume', () => {
|
|
this.set({ isPlaying: true });
|
|
});
|
|
current.replayer.on('finish', () => {
|
|
this.timer = null;
|
|
this.set({ isPlaying: false, currentTime: 0 });
|
|
});
|
|
current.replayer.on('skip-start', payload => {
|
|
payload.isSkipping = true;
|
|
this.set(payload);
|
|
});
|
|
current.replayer.on('skip-end', payload => {
|
|
payload.isSkipping = false;
|
|
this.set(payload);
|
|
});
|
|
}
|
|
if (changed.isPlaying) {
|
|
if (current.isPlaying && !this.timer) {
|
|
this.loopTimer();
|
|
}
|
|
}
|
|
if (changed.skipInactive) {
|
|
current.replayer.setConfig({ skipInactive: current.skipInactive });
|
|
}
|
|
},
|
|
ondestroy() {
|
|
const { isPlaying } = this.get();
|
|
if (isPlaying) {
|
|
this.pause();
|
|
}
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style>
|
|
.rr-controller {
|
|
width: 100%;
|
|
height: 80px;
|
|
background: #fff;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-around;
|
|
align-items: center;
|
|
border-radius: 0 0 5px 5px;
|
|
}
|
|
|
|
.rr-timeline {
|
|
width: 80%;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.rr-timeline__time {
|
|
padding: 0 20px;
|
|
color: #11103e;
|
|
}
|
|
|
|
.rr-progress {
|
|
width: 100%;
|
|
height: 12px;
|
|
background: #eee;
|
|
position: relative;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
box-sizing: border-box;
|
|
border-top: solid 4px #fff;
|
|
border-bottom: solid 4px #fff;
|
|
}
|
|
|
|
.rr-progress.disabled {
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.rr-progress__step {
|
|
height: 100%;
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
background: #e0e1fe;
|
|
}
|
|
|
|
.rr-progress__handler {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 10px;
|
|
position: absolute;
|
|
top: 2px;
|
|
transform: translate(-50%, -50%);
|
|
background: rgb(73, 80, 246);
|
|
}
|
|
|
|
.rr-controller__btns {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.rr-controller__btns button {
|
|
width: 32px;
|
|
height: 32px;
|
|
display: flex;
|
|
padding: 0;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: none;
|
|
border: none;
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.rr-controller__btns button:active {
|
|
background: #e0e1fe;
|
|
}
|
|
|
|
.rr-controller__btns button.active {
|
|
color: #fff;
|
|
background: rgb(73, 80, 246);
|
|
}
|
|
|
|
.rr-controller__btns button:disabled {
|
|
cursor: not-allowed;
|
|
}
|
|
</style>
|