<template>
<div class="canvas-video">
<canvas
ref="myCanvasByVideo"
class="myCanvas"
id="myCanvasByVideo"
:width="width"
:height="height"
></canvas>
<div class="btnDiv">
<div
v-if="!isPlayVideo && !isStartVideo"
class="playback"
@click.stop="playVideo"
>
<div>回放</div>
<img src="@/assets/image/play.png" alt="" />
</div>
<div
v-if="isPlayVideo && isStartVideo"
class="playback"
@click.stop="pauseVideo"
>
<div>暂停</div>
<img src="@/assets/image/pause.png" alt="" />
</div>
<div
v-if="!isPlayVideo && isStartVideo"
class="playback"
@click.stop="continueVideo"
>
<div>继续</div>
<img src="@/assets/image/play.png" alt="" />
</div>
<div class="rocket">
<img
v-show="isRocket"
src="@/assets/image/rocket.png"
alt=""
@click="playRocket"
/>
<img
v-show="!isRocket"
src="@/assets/image/rocket_noChoose.png"
alt=""
@click="playRocket"
/>
</div>
<div class="mySlider">
<el-slider
v-model="nowTime"
:max="allTime"
@change="changeVideoSilder"
></el-slider>
</div>
<div class="myTime">
{{ getFormatTime(nowTime) }} / {{ getFormatTime(allTime) }}
</div>
<div class="mySpeed">
<div @click.stop="isShowSpeedBox = !isShowSpeedBox">
{{ speedList.filter((item) => item.value === nowSpeed)[0].name }}
</div>
<div class="speedList" v-show="isShowSpeedBox">
<div
class="speedItem"
:class="item.value === nowSpeed ? 'active' : ''"
v-for="(item, index) in speedList"
:key="index"
@click.stop="changeSpeed(item.value)"
>
{{ item.name }}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "canvasVideo",
components: {},
props: {
width: {
type: Number,
default: 500,
},
height: {
type: Number,
default: 500,
},
lineWidth: {
type: Number,
default: 1,
},
backgroundColor: {
type: String,
default: "black",
},
color: {
type: String,
default: "red",
},
pointData: {
type: Array,
default: [
[
{
x: 144.42779541015625,
y: 112.7576904296875,
time: 1702536449825,
},
],
],
},
},
data() {
return {
canvasHistory: null,
myCanvasByVideo: null, // 视频播放画布
ctxByVideo: null, // 视频播放画布
drawLineTimer: null,
drawStepTimer: null,
isPlayVideo: false, // 是否正在播放
isStartVideo: false, // 是否开始播放
nowTime: 0, // 当前播放时间
allTime: 0, // 回放总时间
nowPoints: [], // 当前学生视频的所有绘制点坐标
indexStep: 0, // 当前播放绘制线条的下标
indexPoint: 0, // 当前播放绘制点的下标
nowTimer: null, // 计算当前播放时间的定时器
isShowSpeedBox: false, // 是否展示速度调整列表
nowSpeed: 1, // 当前速度
speedList: [
{
name: "3X",
value: 3,
},
{
name: "2X",
value: "2",
},
{
name: "1.5X",
value: 1.5,
},
{
name: "1X",
value: 1,
},
{
name: "0.5X",
value: 0.5,
},
],
isRocket: false, // 是否快速播放
gdbl: 2.4583, // 两种纸的坐标对应比例 小纸 2.4583 = 4720/1920 大纸 2.9167 = 5600/1920
};
},
mounted() {
this.nowPoints = [
[
{
x: 144.42779541015625,
y: 112.7576904296875,
time: 1702536449825,
},
{
x: 144.42779541015625,
y: 122.65827941894531,
time: 1702536449857,
},
{
x: 144.42779541015625,
y: 128.27886962890625,
time: 1702536449871,
},
{
x: 144.42779541015625,
y: 134.2593231201172,
time: 1702536449889,
},
{
x: 144.42779541015625,
y: 146.72254943847656,
time: 1702536449939,
},
{
x: 144.42779541015625,
y: 157.4418182373047,
time: 1702536449955,
},
{
x: 145.78329467773438,
y: 162.1194610595703,
time: 1702536449971,
},
{
x: 145.76043701171875,
y: 167.31605529785156,
time: 1702536449989,
},
{
x: 146.4267578125,
y: 172.4877471923828,
time: 1702536450007,
},
{
x: 146.4267578125,
y: 177.4159698486328,
time: 1702536450020,
},
{
x: 146.4267578125,
y: 182.0648651123047,
time: 1702536450039,
},
{
x: 147.09307861328125,
y: 187.20314025878906,
time: 1702536450053,
},
{
x: 147.762451171875,
y: 192.70411682128906,
time: 1702536450070,
},
{
x: 148.41329956054688,
y: 197.30494689941406,
time: 1702536450088,
},
{
x: 149.09210205078125,
y: 202.69898986816406,
time: 1702536450103,
},
{
x: 149.7584228515625,
y: 207.8609161376953,
time: 1702536450121,
},
{
x: 150.42477416992188,
y: 212.64430236816406,
time: 1702536450140,
},
{
x: 151.09112548828125,
y: 217.8363494873047,
time: 1702536450158,
},
{
x: 151.09112548828125,
y: 223.17628479003906,
time: 1702536450178,
},
{
x: 151.7574462890625,
y: 229.3798065185547,
time: 1702536450194,
},
{
x: 151.7574462890625,
y: 234.6603240966797,
time: 1702536450204,
},
{
x: 151.7574462890625,
y: 239.9730987548828,
time: 1702536450221,
},
{
x: 151.7574462890625,
y: 245.2152862548828,
time: 1702536450246,
},
{
x: 151.7574462890625,
y: 251.24928283691406,
time: 1702536450257,
},
{
x: 151.09112548828125,
y: 257.2041473388672,
time: 1702536450270,
},
{
x: 151.09112548828125,
y: 261.8277130126953,
time: 1702536450288,
},
{
x: 151.09112548828125,
y: 267.2612762451172,
time: 1702536450308,
},
{
x: 151.09112548828125,
y: 273.2233123779297,
time: 1702536450322,
},
{
x: 151.09112548828125,
y: 279.8961639404297,
time: 1702536450342,
},
{
x: 151.09112548828125,
y: 285.8682403564453,
time: 1702536450358,
},
{
x: 152.39996337890625,
y: 291.18141174316406,
time: 1702536450370,
},
{
x: 153.7384033203125,
y: 297.18141174316406,
time: 1702536450389,
},
{
x: 155.09494018554688,
y: 303.9618377685547,
time: 1702536450405,
},
{
x: 156.41146850585938,
y: 309.8865203857422,
time: 1702536450420,
},
{
x: 158.44903564453125,
y: 314.70338439941406,
time: 1702536450442,
},
{
x: 161.03179931640625,
y: 319.8107147216797,
time: 1702536450455,
},
{
x: 163.73126220703125,
y: 325.8739776611328,
time: 1702536450473,
},
{
x: 165.68637084960938,
y: 331.1151580810547,
time: 1702536450488,
},
{
x: 168.38128662109375,
y: 336.5032501220703,
time: 1702536450503,
},
{
x: 171.0692138671875,
y: 341.2170867919922,
time: 1702536450521,
},
{
x: 173.73162841796875,
y: 345.2093963623047,
time: 1702536450538,
},
{
x: 176.42041015625,
y: 348.57081604003906,
time: 1702536450554,
},
{
x: 179.12631225585938,
y: 351.2758026123047,
time: 1702536450571,
},
{
x: 181.71502685546875,
y: 353.8636932373047,
time: 1702536450588,
},
{
x: 184.35678100585938,
y: 355.19776916503906,
time: 1702536450605,
},
{
x: 187.10076904296875,
y: 356.5692901611328,
time: 1702536450621,
},
{
x: 188.40576171875,
y: 357.2216033935547,
time: 1702536450636,
},
],
[
{
x: 278.3201904296875,
y: 225.33656311035156,
time: 1702536451411,
},
{
x: 290.136962890625,
y: 225.3314971923828,
time: 1702536451431,
},
{
x: 305.58203125,
y: 225.9976043701172,
time: 1702536451442,
},
{
x: 319.9619140625,
y: 225.9976043701172,
time: 1702536451457,
},
{
x: 331.8698425292969,
y: 226.6862030029297,
time: 1702536451472,
},
{
x: 342.2525329589844,
y: 227.32032775878906,
time: 1702536451488,
},
{
x: 351.42987060546875,
y: 227.32984924316406,
time: 1702536451504,
},
{
x: 361.1939697265625,
y: 227.32984924316406,
time: 1702536451521,
},
{
x: 370.506103515625,
y: 226.6363983154297,
time: 1702536451540,
},
{
x: 377.578857421875,
y: 226.01075744628906,
time: 1702536451585,
},
{
x: 396.9224853515625,
y: 223.3396759033203,
time: 1702536451589,
},
{
x: 404.81201171875,
y: 222.0262908935547,
time: 1702536451603,
},
{
x: 412.2171630859375,
y: 220.68153381347656,
time: 1702536451621,
},
{
x: 418.94720458984375,
y: 219.33851623535156,
time: 1702536451637,
},
{
x: 424.103515625,
y: 218.0503692626953,
time: 1702536451654,
},
{
x: 429.489013671875,
y: 218.00428771972656,
time: 1702536451671,
},
{
x: 433.5123291015625,
y: 218.00428771972656,
time: 1702536451687,
},
{
x: 436.1656494140625,
y: 217.33815002441406,
time: 1702536451703,
},
{
x: 438.26458740234375,
y: 217.33815002441406,
time: 1702536451720,
},
{
x: 439.58154296875,
y: 217.33815002441406,
time: 1702536451740,
},
{
x: 439.613525390625,
y: 217.33815002441406,
time: 1702536451761,
},
],
[
{
x: 359.6776123046875,
y: 148.3515167236328,
time: 1702536452241,
},
{
x: 360.95751953125,
y: 166.9501495361328,
time: 1702536452258,
},
{
x: 361.6524658203125,
y: 186.5827178955078,
time: 1702536452270,
},
{
x: 362.3128662109375,
y: 203.9375762939453,
time: 1702536452288,
},
{
x: 362.9931640625,
y: 218.0919647216797,
time: 1702536452306,
},
{
x: 364.331787109375,
y: 232.8119354248047,
time: 1702536452320,
},
{
x: 364.984130859375,
y: 247.2077178955078,
time: 1702536452337,
},
{
x: 365.6175537109375,
y: 260.9715118408203,
time: 1702536452354,
},
{
x: 366.2884521484375,
y: 275.6442413330078,
time: 1702536452371,
},
{
x: 367.6258544921875,
y: 289.0420379638672,
time: 1702536452390,
},
{
x: 368.8919677734375,
y: 301.1228790283203,
time: 1702536452403,
},
{
x: 370.292724609375,
y: 313.0811309814453,
time: 1702536452421,
},
{
x: 372.9156494140625,
y: 323.0240936279297,
time: 1702536452438,
},
{
x: 374.9552001953125,
y: 330.4568634033203,
time: 1702536452455,
},
{
x: 377.5841064453125,
y: 337.0873565673828,
time: 1702536452471,
},
{
x: 378.9256591796875,
y: 342.3609161376953,
time: 1702536452488,
},
{
x: 380.2994384765625,
y: 347.8545379638672,
time: 1702536452505,
},
{
x: 381.62762451171875,
y: 351.8480682373047,
time: 1702536452520,
},
{
x: 382.308837890625,
y: 354.4903106689453,
time: 1702536452545,
},
{
x: 382.308837890625,
y: 354.55714416503906,
time: 1702536452556,
},
],
[
{
x: 586.8844604492188,
y: 151.2805633544922,
time: 1702536453141,
},
{
x: 585.511962890625,
y: 165.02943420410156,
time: 1702536453157,
},
{
x: 584.8742065429688,
y: 183.56068420410156,
time: 1702536453173,
},
{
x: 584.8742065429688,
y: 201.2294158935547,
time: 1702536453188,
},
{
x: 584.8742065429688,
y: 215.3689422607422,
time: 1702536453204,
},
{
x: 584.8742065429688,
y: 230.58912658691406,
time: 1702536453222,
},
{
x: 585.54052734375,
y: 243.74464416503906,
time: 1702536453239,
},
{
x: 586.8107299804688,
y: 256.01344299316406,
time: 1702536453256,
},
{
x: 587.5194091796875,
y: 267.7810821533203,
time: 1702536453271,
},
{
x: 588.8597412109375,
y: 279.1865692138672,
time: 1702536453288,
},
{
x: 590.2003784179688,
y: 289.90699768066406,
time: 1702536453304,
},
{
x: 591.52001953125,
y: 301.1284942626953,
time: 1702536453321,
},
{
x: 592.8444213867188,
y: 311.0792694091797,
time: 1702536453339,
},
{
x: 594.8500366210938,
y: 320.5182342529297,
time: 1702536453355,
},
{
x: 596.1693115234375,
y: 328.3832244873047,
time: 1702536453371,
},
{
x: 597.5363159179688,
y: 336.58277893066406,
time: 1702536453390,
},
{
x: 598.8732299804688,
y: 343.2628936767578,
time: 1702536453404,
},
{
x: 600.2008666992188,
y: 348.56568908691406,
time: 1702536453420,
},
{
x: 600.8662109375,
y: 353.1544647216797,
time: 1702536453438,
},
{
x: 600.8662109375,
y: 356.4942169189453,
time: 1702536453454,
},
{
x: 600.8662109375,
y: 358.5269012451172,
time: 1702536453472,
},
{
x: 600.8662109375,
y: 358.5537872314453,
time: 1702536453482,
},
],
[
{
x: 679.638916015625,
y: 223.9750518798828,
time: 1702536453873,
},
{
x: 698.049560546875,
y: 220.69044494628906,
time: 1702536453889,
},
{
x: 715.046142578125,
y: 218.70338439941406,
time: 1702536453912,
},
{
x: 731.1329956054688,
y: 217.30799865722656,
time: 1702536453922,
},
{
x: 743.7023315429688,
y: 216.6453094482422,
time: 1702536453939,
},
{
x: 755.0431518554688,
y: 216.6720428466797,
time: 1702536453957,
},
{
x: 764.043701171875,
y: 216.6720428466797,
time: 1702536453972,
},
{
x: 770.6744995117188,
y: 216.6720428466797,
time: 1702536453988,
},
{
x: 776.098388671875,
y: 216.6720428466797,
time: 1702536454005,
},
{
x: 780.066650390625,
y: 216.6720428466797,
time: 1702536454021,
},
{
x: 783.4193115234375,
y: 216.6720428466797,
time: 1702536454041,
},
{
x: 785.4308471679688,
y: 216.6720428466797,
time: 1702536454057,
},
{
x: 786.7714233398438,
y: 216.6720428466797,
time: 1702536454070,
},
{
x: 786.7733154296875,
y: 216.6720428466797,
time: 1702536454083,
},
],
[
{
x: 733.5531616210938,
y: 316.62501525878906,
time: 1702536454456,
},
{
x: 756.4210815429688,
y: 315.2781524658203,
time: 1702536454472,
},
{
x: 775.4960327148438,
y: 315.2565460205078,
time: 1702536454488,
},
{
x: 790.3649291992188,
y: 317.1736297607422,
time: 1702536454506,
},
{
x: 804.3414916992188,
y: 319.8348846435547,
time: 1702536454524,
},
{
x: 815.0912475585938,
y: 323.1543731689453,
time: 1702536454540,
},
{
x: 823.2052612304688,
y: 325.8422393798828,
time: 1702536454556,
},
{
x: 829.9567260742188,
y: 328.5275115966797,
time: 1702536454571,
},
{
x: 835.1862182617188,
y: 329.8536834716797,
time: 1702536454589,
},
{
x: 839.3932495117188,
y: 330.5771026611328,
time: 1702536454606,
},
{
x: 841.41259765625,
y: 330.5771026611328,
time: 1702536454618,
},
],
[
{
x: 783.1222534179688,
y: 122.69233703613281,
time: 1702536454959,
},
{
x: 807.8834838867188,
y: 125.26026916503906,
time: 1702536454974,
},
{
x: 833.0866088867188,
y: 128.6871337890625,
time: 1702536454989,
},
{
x: 854.1300659179688,
y: 132.88661193847656,
time: 1702536455006,
},
{
x: 869.70361328125,
y: 138.1724853515625,
time: 1702536455021,
},
{
x: 882.982666015625,
y: 144.5460968017578,
time: 1702536455042,
},
{
x: 895.916748046875,
y: 152.63514709472656,
time: 1702536455056,
},
{
x: 906.02685546875,
y: 160.70143127441406,
time: 1702536455071,
},
{
x: 916.085693359375,
y: 170.0878448486328,
time: 1702536455090,
},
{
x: 925.06689453125,
y: 182.9326934814453,
time: 1702536455105,
},
{
x: 932.54638671875,
y: 195.7455291748047,
time: 1702536455121,
},
{
x: 937.921142578125,
y: 209.0699920654297,
time: 1702536455139,
},
{
x: 941.3206787109375,
y: 221.79115295410156,
time: 1702536455159,
},
{
x: 943.363037109375,
y: 235.3358612060547,
time: 1702536455171,
},
{
x: 943.3616943359375,
y: 248.3235626220703,
time: 1702536455187,
},
{
x: 942.05517578125,
y: 261.0668182373047,
time: 1702536455205,
},
{
x: 938.763427734375,
y: 275.0695037841797,
time: 1702536455221,
},
{
x: 933.5555419921875,
y: 287.5211639404297,
time: 1702536455238,
},
{
x: 928.054443359375,
y: 298.5670928955078,
time: 1702536455255,
},
{
x: 921.485107421875,
y: 307.7720489501953,
time: 1702536455271,
},
{
x: 913.521728515625,
y: 316.4197235107422,
time: 1702536455288,
},
{
x: 905.4949951171875,
y: 325.1339569091797,
time: 1702536455305,
},
{
x: 896.376953125,
y: 333.6233673095703,
time: 1702536455321,
},
{
x: 885.5185546875,
y: 342.4713592529297,
time: 1702536455339,
},
{
x: 874.749755859375,
y: 350.5452117919922,
time: 1702536455356,
},
{
x: 863.4857177734375,
y: 359.8206329345703,
time: 1702536455370,
},
{
x: 852.8712768554688,
y: 367.1151580810547,
time: 1702536455391,
},
{
x: 841.2532958984375,
y: 374.6466522216797,
time: 1702536455404,
},
{
x: 829.65380859375,
y: 381.0709991455078,
time: 1702536455421,
},
{
x: 817.5537109375,
y: 387.79103088378906,
time: 1702536455437,
},
{
x: 805.4242553710938,
y: 393.8605499267578,
time: 1702536455454,
},
{
x: 793.5094604492188,
y: 399.15431213378906,
time: 1702536455472,
},
{
x: 782.9738159179688,
y: 403.1088409423828,
time: 1702536455489,
},
{
x: 772.8463745117188,
y: 406.4949493408203,
time: 1702536455504,
},
{
x: 764.2553100585938,
y: 409.1325225830078,
time: 1702536455522,
},
{
x: 756.42041015625,
y: 411.1269073486328,
time: 1702536455540,
},
{
x: 749.0506591796875,
y: 413.7377471923828,
time: 1702536455557,
},
{
x: 742.9489135742188,
y: 416.42872619628906,
time: 1702536455572,
},
{
x: 737.5338745117188,
y: 418.4693145751953,
time: 1702536455588,
},
{
x: 733.5607299804688,
y: 420.4709014892578,
time: 1702536455605,
},
{
x: 730.177734375,
y: 422.4792022705078,
time: 1702536455622,
},
{
x: 728.816650390625,
y: 424.4844512939453,
time: 1702536455638,
},
{
x: 728.80224609375,
y: 427.1183624267578,
time: 1702536455655,
},
{
x: 732.04443359375,
y: 429.1317901611328,
time: 1702536455671,
},
{
x: 739.4037475585938,
y: 431.8090362548828,
time: 1702536455688,
},
{
x: 752.61962890625,
y: 435.11402893066406,
time: 1702536455712,
},
{
x: 767.9056396484375,
y: 437.7860870361328,
time: 1702536455727,
},
{
x: 784.0560302734375,
y: 439.1490020751953,
time: 1702536455738,
},
{
x: 800.2537841796875,
y: 441.17530822753906,
time: 1702536455755,
},
{
x: 820.0858154296875,
y: 443.8155059814453,
time: 1702536455772,
},
{
x: 837.1102905273438,
y: 445.79103088378906,
time: 1702536455787,
},
{
x: 852.6246948242188,
y: 447.1361846923828,
time: 1702536455803,
},
{
x: 865.9520263671875,
y: 449.1322784423828,
time: 1702536455820,
},
{
x: 876.6416015625,
y: 451.1309356689453,
time: 1702536455844,
},
{
x: 886.6123046875,
y: 453.1099395751953,
time: 1702536455855,
},
{
x: 894.085205078125,
y: 454.4803009033203,
time: 1702536455871,
},
{
x: 899.9359130859375,
y: 455.77760314941406,
time: 1702536455888,
},
{
x: 904.62646484375,
y: 458.4120635986328,
time: 1702536455904,
},
{
x: 908.64892578125,
y: 460.4477081298828,
time: 1702536455920,
},
{
x: 908.71240234375,
y: 460.4689483642578,
time: 1702536455930,
},
],
];
// this.nowPoints = this.pointData
this.initCanvasByVideo();
this.allTime = Math.ceil(
(this.nowPoints[this.nowPoints.length - 1][
this.nowPoints[this.nowPoints.length - 1].length - 1
].time -
this.nowPoints[0][0].time) /
1000
); // 最后一个坐标的时间 - 第一个坐标的时间
},
methods: {
// 快速播放
async playRocket() {
// 把所有状态清零
this.isPlayVideo = false;
this.isStartVideo = false;
this.indexStep = 0;
this.indexPoint = 0;
if (this.nowTimer) {
clearInterval(this.nowTimer);
}
if (this.drawStepTimer) {
clearTimeout(this.drawStepTimer);
}
if (this.drawLineTimer) {
clearTimeout(this.drawLineTimer);
}
this.nowTime = 0;
if (this.isRocket) {
this.isRocket = false;
return;
}
this.isPlayVideo = false;
this.isRocket = true;
this.ctxByVideo.strokeStyle = this.color;
this.ctxByVideo.translate(0.5, 0.5); // 抗锯齿(好像没得卵用)
this.ctxByVideo.antialias = "smooth"; // 抗锯齿(好像没得卵用)
this.ctxByVideo.imageSmoothingEnabled = true; // 抗锯齿(好像没得卵用)
this.ctxByVideo.lineJoin = "round"; // 设置圆角
this.ctxByVideo.lineWidth = this.lineWidth; // 设置粗细
this.ctxByVideo.shadowBlur = 1;
this.ctxByVideo.shadowColor = this.color;
this.ctxByVideo.clearRect(
0,
0,
this.myCanvasByVideo.width,
this.myCanvasByVideo.height
); // 清空
if (this.drawStepTimer) {
clearTimeout(this.drawStepTimer);
}
if (this.drawLineTimer) {
clearTimeout(this.drawLineTimer);
}
for (let j = 0; j < this.nowPoints.length; j++) {
this.indexStep = j;
this.ctxByVideo.beginPath();
let timeout = 0;
if (j !== 0) {
if (this.nowPoints[j].length && this.nowPoints[j - 1].length) {
timeout =
this.nowPoints[j][0].time -
this.nowPoints[j - 1][this.nowPoints[j - 1].length - 1].time;
}
}
await this.drawStepByRocket(this.nowPoints[j], 50);
}
this.isRocket = false;
},
//答题笔迹
async playVideo() {
if (this.isRocket) {
this.isPlayVideo = false;
clearInterval(this.nowTimer);
clearTimeout(this.drawLineTimer);
clearTimeout(this.drawStepTimer);
this.myCanvasByVideo.width = this.myCanvasByVideo.width; // 清空
this.canvasHistory = null;
// 清空操作
this.nowTime = 0;
this.isPlayVideo = false;
this.isStartVideo = false;
this.indexStep = 0;
this.indexPoint = 0;
clearInterval(this.nowTimer);
this.canvasHistory = null;
if (this.nowTimer) {
clearInterval(this.nowTimer);
}
if (this.drawStepTimer) {
clearTimeout(this.drawStepTimer);
}
if (this.drawLineTimer) {
clearTimeout(this.drawLineTimer);
}
}
this.isRocket = false;
this.isPlayVideo = true;
this.isStartVideo = true;
this.ctxByVideo.strokeStyle = this.color;
this.ctxByVideo.translate(0.5, 0.5); // 抗锯齿(好像没得卵用)
this.ctxByVideo.antialias = "smooth"; // 抗锯齿(好像没得卵用)
this.ctxByVideo.imageSmoothingEnabled = true; // 抗锯齿(好像没得卵用)
this.ctxByVideo.lineJoin = "round"; // 设置圆角
this.ctxByVideo.lineWidth = this.lineWidth; // 设置粗细
this.ctxByVideo.shadowBlur = 1;
this.ctxByVideo.shadowColor = this.color;
this.ctxByVideo.clearRect(
0,
0,
this.myCanvasByVideo.width,
this.myCanvasByVideo.height
); // 清空
this.nowTimer = setInterval(() => {
if (this.nowTime < this.allTime) {
this.nowTime++;
}
}, 1000 / this.nowSpeed);
for (let j = 0; j < this.nowPoints.length; j++) {
this.indexStep = j;
this.ctxByVideo.beginPath();
let timeout = 0;
if (j !== 0) {
if (this.nowPoints[j].length && this.nowPoints[j - 1].length) {
timeout =
this.nowPoints[j][0].time -
this.nowPoints[j - 1][this.nowPoints[j - 1].length - 1].time;
}
}
await this.drawStep(this.nowPoints[j], timeout / this.nowSpeed);
}
this.nowTime = this.allTime;
this.isPlayVideo = false;
clearInterval(this.nowTimer);
},
// 暂停播放
pauseVideo() {
this.isPlayVideo = false;
clearInterval(this.nowTimer);
clearTimeout(this.drawLineTimer);
clearTimeout(this.drawStepTimer);
this.ctxByVideo.stroke();
},
// 继续播放
async continueVideo() {
if (this.nowTime === this.allTime) {
// 播放完了重新播放
this.nowTime = 0;
this.playVideo();
return;
}
this.isPlayVideo = true;
let startIndex = JSON.parse(JSON.stringify(this.indexStep));
this.nowTimer = setInterval(() => {
if (this.nowTime < this.allTime) {
this.nowTime++;
}
}, 1000 / this.nowSpeed);
console.log("从这开始", this.indexStep, this.nowPoints);
// this.ctx.moveTo(0, 0)
// this.ctx.lineTo(500, 500)
// this.ctx.stroke();
// this.ctx.closePath();
for (let j = startIndex; j < this.nowPoints.length; j++) {
this.indexStep = j;
let timeout = 0;
if (j !== 0) {
if (this.nowPoints[j].length && this.nowPoints[j - 1].length) {
timeout =
this.nowPoints[j][0].time -
this.nowPoints[j - 1][this.nowPoints[j - 1].length - 1].time;
}
}
await this.drawStep(this.nowPoints[j], timeout / this.nowSpeed);
}
this.nowTime = this.allTime;
clearInterval(this.nowTimer);
this.isPlayVideo = false;
},
// 改变进度条(根据当前时间获取)
async changeVideoSilder() {
console.log("改变了");
clearInterval(this.nowTimer);
clearTimeout(this.drawLineTimer);
clearTimeout(this.drawStepTimer);
this.ctxByVideo.clearRect(
0,
0,
this.myCanvasByVideo.width,
this.myCanvasByVideo.height
); // 清空
this.ctxByVideo.strokeStyle = this.color;
this.ctxByVideo.translate(0.5, 0.5); // 抗锯齿(好像没得卵用)
this.ctxByVideo.antialias = "smooth"; // 抗锯齿(好像没得卵用)
this.ctxByVideo.imageSmoothingEnabled = true; // 抗锯齿(好像没得卵用)
this.ctxByVideo.lineJoin = "round"; // 设置圆角
this.ctxByVideo.lineWidth = this.lineWidth; // 设置粗细
this.ctxByVideo.shadowBlur = 1;
this.ctxByVideo.shadowColor = this.color;
let allTime = 0;
// 直接把进度条当前定位之前的画出来不加任何延时
here: for (let i = 0; i < this.nowPoints.length; i++) {
this.ctxByVideo.beginPath();
if (i) {
allTime +=
this.nowPoints[i][0].time -
this.nowPoints[i - 1][this.nowPoints[i - 1].length - 1].time;
console.log("1级时间", allTime, i);
}
for (let j = 0; j < this.nowPoints[i].length; j++) {
if (j) {
allTime +=
this.nowPoints[i][j].time - this.nowPoints[i][j - 1].time;
console.log("2级时间", allTime, i, j);
}
if (j !== this.nowPoints[i].length - 1) {
this.ctxByVideo.moveTo(
this.nowPoints[i][j].x / this.gdbl,
this.nowPoints[i][j].y / this.gdbl
);
this.ctxByVideo.lineTo(
this.nowPoints[i][j + 1].x / this.gdbl,
this.nowPoints[i][j + 1].y / this.gdbl
);
this.ctxByVideo.stroke();
this.ctxByVideo.closePath();
}
if (allTime >= this.nowTime * 1000) {
this.indexStep = i;
this.indexPoint = j;
break here;
}
}
}
if (this.isPlayVideo) {
// 如果是播放状态 则继续播放
this.continueVideo();
}
},
drawStep(data, time) {
return new Promise((resolve) => {
this.drawStepTimer = setTimeout(async () => {
let startIndex = JSON.parse(JSON.stringify(this.indexPoint));
for (let i = startIndex; i < data.length - 1; i++) {
this.indexPoint = i;
let timeLine = data[i + 1].time - data[i].time;
if (timeLine) {
// 点阵笔有很多点位不同但时间相同的点 不做异步处理
await this.drawLine(
data[i].x / this.gdbl,
data[i].y / this.gdbl,
data[i + 1].x / this.gdbl,
data[i + 1].y / this.gdbl,
(data[i].x + data[i + 1].x) / 2 / this.gdbl,
(data[i].y + data[i + 1].y) / 2 / this.gdbl,
timeLine / this.nowSpeed
);
} else {
this.ctxByVideo.moveTo(
data[i].x / this.gdbl,
data[i].y / this.gdbl
);
this.ctxByVideo.lineTo(
data[i + 1].x / this.gdbl,
data[i + 1].y / this.gdbl
);
this.ctxByVideo.stroke();
this.ctxByVideo.closePath();
}
}
this.indexPoint = 0;
this.ctxByVideo.stroke();
resolve();
}, time);
});
},
// 相同速度播放
drawStepByRocket(data, time) {
return new Promise((resolve) => {
this.drawStepTimer = setTimeout(async () => {
let startIndex = JSON.parse(JSON.stringify(this.indexPoint));
for (let i = startIndex; i < data.length - 1; i++) {
this.indexPoint = i;
let timeLine = data[i + 1].time - data[i].time;
if (timeLine) {
// 点阵笔有很多点位不同但时间相同的点 不做异步处理
await this.drawLine(
data[i].x / this.gdbl,
data[i].y / this.gdbl,
data[i + 1].x / this.gdbl,
data[i + 1].y / this.gdbl,
(data[i].x + data[i + 1].x) / 2 / this.gdbl,
(data[i].y + data[i + 1].y) / 2 / this.gdbl,
5
);
} else {
this.ctxByVideo.moveTo(
data[i].x / this.gdbl,
data[i].y / this.gdbl
);
this.ctxByVideo.lineTo(
data[i + 1].x / this.gdbl,
data[i + 1].y / this.gdbl
);
this.ctxByVideo.stroke();
this.ctxByVideo.closePath();
}
}
this.indexPoint = 0;
this.ctxByVideo.stroke();
resolve();
}, time);
});
},
drawLine(x1, y1, x2, y2, controlX, controlY, time) {
return new Promise((resolve) => {
this.drawLineTimer = setTimeout(() => {
this.ctxByVideo.moveTo(x1, y1);
this.ctxByVideo.lineTo(x2, y2);
// this.ctx.quadraticCurveTo(controlX, controlY, x2, y2)
this.ctxByVideo.stroke();
this.ctxByVideo.closePath();
resolve();
}, time);
});
},
// 改变播放速度
changeSpeed(speed) {
this.nowSpeed = speed;
this.isShowSpeedBox = false;
if (this.nowTimer) {
clearInterval(this.nowTimer);
if (this.isPlayVideo) {
this.nowTimer = setInterval(() => {
if (this.nowTime < this.allTime) {
this.nowTime++;
}
}, 1000 / this.nowSpeed);
}
}
},
getFormatTime(second) {
this.formatSecond(second);
},
initCanvasByVideo() {
this.myCanvasByVideo = document.getElementById("myCanvasByVideo");
this.ctxByVideo = this.myCanvasByVideo.getContext("2d");
},
formatSecond(allSecond) {
let minute = 0;
let second = 0;
second = allSecond % 60;
if (second < 10) {
second = "0" + second;
}
minute = Math.trunc(allSecond / 60);
if (minute < 10) {
minute = "0" + minute;
}
return minute + ":" + second;
},
},
};
</script>
<style lang="scss" scoped>
.canvas-video {
width: 100%;
height: 100%;
.myCanvas {
background: black;
}
.btnDiv {
display: flex;
justify-content: center;
margin-top: 5px;
.playback {
display: flex;
justify-content: space-between;
color: #ffffff;
text-align: center;
padding: 0 15px;
border-radius: 4px;
background: #00b386;
height: 28px;
line-height: 28px;
cursor: pointer;
img {
width: 10px;
height: 14px;
margin-top: 7px;
margin-left: 12px;
}
}
.rocket {
margin-top: 3px;
margin-left: 10px;
cursor: pointer;
}
.mySlider {
width: 500px;
margin-left: 20px;
::v-deep .el-slider__bar {
border-radius: 17px;
background: #00b386;
}
::v-deep .el-slider__button {
border: 1px solid #00b386;
}
::v-deep .el-slider__runway {
border-radius: 17px;
background: #444;
}
}
.myTime {
margin-left: 14px;
color: #a9a9a9;
line-height: 35px;
}
.mySpeed {
cursor: pointer;
position: relative;
border-radius: 2px;
background: #ef8714;
width: 60px;
height: 20px;
line-height: 20px;
text-align: center;
color: #ffffff;
margin-left: 20px;
margin-top: 5px;
.speedList {
position: absolute;
bottom: 20px;
border-radius: 2px;
background: #232322;
padding: 10px 0;
width: 60px;
.speedItem {
width: 100%;
text-align: center;
margin-bottom: 10px;
color: #ffffff;
}
.speedItem.active {
color: #ef8714;
font-size: 14px;
}
.speedItem:last-child {
margin-bottom: 0px;
}
z-index: 2000;
}
}
.slider-warpper {
width: 320px;
height: 16px;
position: absolute;
left: 280px;
// background: #ef9e00 !important;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.slider-content {
width: 304px;
height: 16px;
background: #2d2d2d;
border: 1px solid rgba(255, 186, 33, 0.16);
border-radius: 22px;
position: relative;
.slider {
border-radius: 22px;
position: absolute;
left: 0;
top: 0;
width: 30px;
height: 16px;
background: #ffba21;
}
}
.persent {
margin-left: 10px;
color: #ffba21;
font-size: 18px;
font-weight: 600;
letter-spacing: 1.26px;
}
}
}
}
</style>
?
<template>
<div class="canvas-video">
<canvas ref="myCanvasByVideo" class="myCanvas" id="myCanvasByVideo" :width="props.width" :height="props.height"></canvas>
<div class="btnDiv">
<div v-if="!state.isPlayVideo && !state.isStartVideo" class="playback" @click.stop="playVideo">
<div>回放</div>
</div>
<div v-if="state.isPlayVideo && state.isStartVideo" class="playback" @click.stop="pauseVideo">
<div>暂停</div>
</div>
<div v-if="!state.isPlayVideo && state.isStartVideo" class="playback" @click.stop="continueVideo">
<div>继续</div>
</div>
<!-- <div class="rocket">
<img v-show="state.isRocket" src="@/assets/image/rocket.png" alt="" @click="playRocket">
<img v-show="!state.isRocket" src="@/assets/image/rocket_noChoose.png" alt="" @click="playRocket">
</div> -->
<div class="mySlider">
<el-slider v-model="state.nowTime" :max="state.allTime" @change="changeVideoSilder"></el-slider>
</div>
<div class="myTime">{{getFormatTime(state.nowTime)}} / {{getFormatTime(state.allTime)}}</div>
<div class="mySpeed">
<div @click.stop="state.isShowSpeedBox = !state.isShowSpeedBox">{{state.speedList.filter((item:any) => item.value === state.nowSpeed)[0].name}}</div>
<div class="speedList" v-show="state.isShowSpeedBox">
<div class="speedItem" :class="item.value === state.nowSpeed ? 'active' : ''" v-for="(item, index) in state.speedList" :key="index" @click.stop="changeSpeed(item.value)">
{{item.name}}
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive } from 'vue'
type TProps = {
width: number
height: number,
lineWidth: number,
backgroundColor: string,
color: string,
pointData: any
}
const props = withDefaults(defineProps<TProps>(), {})
const state = reactive<any>({
minute:0,
second:0,
canvasHistory: null,
myCanvasByVideo: null, // 视频播放画布
ctxByVideo: null, // 视频播放画布
drawLineTimer: null,
drawStepTimer: null,
isPlayVideo: false, // 是否正在播放
isStartVideo: false, // 是否开始播放
nowTime: 0, // 当前播放时间
allTime: 0, // 回放总时间
nowPoints: [], // 当前学生视频的所有绘制点坐标
indexStep: 0, // 当前播放绘制线条的下标
indexPoint: 0, // 当前播放绘制点的下标
nowTimer: null, // 计算当前播放时间的定时器
isShowSpeedBox: false, // 是否展示速度调整列表
nowSpeed: 1, // 当前速度
speedList: [{
name: '3X',
value: 3
}, {
name: '2X',
value: '2'
}, {
name: '1.5X',
value: 1.5
}, {
name: '1X',
value: 1
}, {
name: '0.5X',
value: 0.5
}],
isRocket: false, // 是否快速播放
gdbl: 2.4583 // 两种纸的坐标对应比例 小纸 2.4583 = 4720/1920 大纸 2.9167 = 5600/1920
})
onMounted(()=> {
state.nowPoints = [
[
{
"x": 144.42779541015625,
"y": 112.7576904296875,
"time": 1702536449825
},
{
"x": 144.42779541015625,
"y": 122.65827941894531,
"time": 1702536449857
},
{
"x": 144.42779541015625,
"y": 128.27886962890625,
"time": 1702536449871
},
{
"x": 144.42779541015625,
"y": 134.2593231201172,
"time": 1702536449889
},
{
"x": 144.42779541015625,
"y": 146.72254943847656,
"time": 1702536449939
},
{
"x": 144.42779541015625,
"y": 157.4418182373047,
"time": 1702536449955
},
{
"x": 145.78329467773438,
"y": 162.1194610595703,
"time": 1702536449971
},
{
"x": 145.76043701171875,
"y": 167.31605529785156,
"time": 1702536449989
},
{
"x": 146.4267578125,
"y": 172.4877471923828,
"time": 1702536450007
},
{
"x": 146.4267578125,
"y": 177.4159698486328,
"time": 1702536450020
},
{
"x": 146.4267578125,
"y": 182.0648651123047,
"time": 1702536450039
},
{
"x": 147.09307861328125,
"y": 187.20314025878906,
"time": 1702536450053
},
{
"x": 147.762451171875,
"y": 192.70411682128906,
"time": 1702536450070
},
{
"x": 148.41329956054688,
"y": 197.30494689941406,
"time": 1702536450088
},
{
"x": 149.09210205078125,
"y": 202.69898986816406,
"time": 1702536450103
},
{
"x": 149.7584228515625,
"y": 207.8609161376953,
"time": 1702536450121
},
{
"x": 150.42477416992188,
"y": 212.64430236816406,
"time": 1702536450140
},
{
"x": 151.09112548828125,
"y": 217.8363494873047,
"time": 1702536450158
},
{
"x": 151.09112548828125,
"y": 223.17628479003906,
"time": 1702536450178
},
{
"x": 151.7574462890625,
"y": 229.3798065185547,
"time": 1702536450194
},
{
"x": 151.7574462890625,
"y": 234.6603240966797,
"time": 1702536450204
},
{
"x": 151.7574462890625,
"y": 239.9730987548828,
"time": 1702536450221
},
{
"x": 151.7574462890625,
"y": 245.2152862548828,
"time": 1702536450246
},
{
"x": 151.7574462890625,
"y": 251.24928283691406,
"time": 1702536450257
},
{
"x": 151.09112548828125,
"y": 257.2041473388672,
"time": 1702536450270
},
{
"x": 151.09112548828125,
"y": 261.8277130126953,
"time": 1702536450288
},
{
"x": 151.09112548828125,
"y": 267.2612762451172,
"time": 1702536450308
},
{
"x": 151.09112548828125,
"y": 273.2233123779297,
"time": 1702536450322
},
{
"x": 151.09112548828125,
"y": 279.8961639404297,
"time": 1702536450342
},
{
"x": 151.09112548828125,
"y": 285.8682403564453,
"time": 1702536450358
},
{
"x": 152.39996337890625,
"y": 291.18141174316406,
"time": 1702536450370
},
{
"x": 153.7384033203125,
"y": 297.18141174316406,
"time": 1702536450389
},
{
"x": 155.09494018554688,
"y": 303.9618377685547,
"time": 1702536450405
},
{
"x": 156.41146850585938,
"y": 309.8865203857422,
"time": 1702536450420
},
{
"x": 158.44903564453125,
"y": 314.70338439941406,
"time": 1702536450442
},
{
"x": 161.03179931640625,
"y": 319.8107147216797,
"time": 1702536450455
},
{
"x": 163.73126220703125,
"y": 325.8739776611328,
"time": 1702536450473
},
{
"x": 165.68637084960938,
"y": 331.1151580810547,
"time": 1702536450488
},
{
"x": 168.38128662109375,
"y": 336.5032501220703,
"time": 1702536450503
},
{
"x": 171.0692138671875,
"y": 341.2170867919922,
"time": 1702536450521
},
{
"x": 173.73162841796875,
"y": 345.2093963623047,
"time": 1702536450538
},
{
"x": 176.42041015625,
"y": 348.57081604003906,
"time": 1702536450554
},
{
"x": 179.12631225585938,
"y": 351.2758026123047,
"time": 1702536450571
},
{
"x": 181.71502685546875,
"y": 353.8636932373047,
"time": 1702536450588
},
{
"x": 184.35678100585938,
"y": 355.19776916503906,
"time": 1702536450605
},
{
"x": 187.10076904296875,
"y": 356.5692901611328,
"time": 1702536450621
},
{
"x": 188.40576171875,
"y": 357.2216033935547,
"time": 1702536450636
}
],
[
{
"x": 278.3201904296875,
"y": 225.33656311035156,
"time": 1702536451411
},
{
"x": 290.136962890625,
"y": 225.3314971923828,
"time": 1702536451431
},
{
"x": 305.58203125,
"y": 225.9976043701172,
"time": 1702536451442
},
{
"x": 319.9619140625,
"y": 225.9976043701172,
"time": 1702536451457
},
{
"x": 331.8698425292969,
"y": 226.6862030029297,
"time": 1702536451472
},
{
"x": 342.2525329589844,
"y": 227.32032775878906,
"time": 1702536451488
},
{
"x": 351.42987060546875,
"y": 227.32984924316406,
"time": 1702536451504
},
{
"x": 361.1939697265625,
"y": 227.32984924316406,
"time": 1702536451521
},
{
"x": 370.506103515625,
"y": 226.6363983154297,
"time": 1702536451540
},
{
"x": 377.578857421875,
"y": 226.01075744628906,
"time": 1702536451585
},
{
"x": 396.9224853515625,
"y": 223.3396759033203,
"time": 1702536451589
},
{
"x": 404.81201171875,
"y": 222.0262908935547,
"time": 1702536451603
},
{
"x": 412.2171630859375,
"y": 220.68153381347656,
"time": 1702536451621
},
{
"x": 418.94720458984375,
"y": 219.33851623535156,
"time": 1702536451637
},
{
"x": 424.103515625,
"y": 218.0503692626953,
"time": 1702536451654
},
{
"x": 429.489013671875,
"y": 218.00428771972656,
"time": 1702536451671
},
{
"x": 433.5123291015625,
"y": 218.00428771972656,
"time": 1702536451687
},
{
"x": 436.1656494140625,
"y": 217.33815002441406,
"time": 1702536451703
},
{
"x": 438.26458740234375,
"y": 217.33815002441406,
"time": 1702536451720
},
{
"x": 439.58154296875,
"y": 217.33815002441406,
"time": 1702536451740
},
{
"x": 439.613525390625,
"y": 217.33815002441406,
"time": 1702536451761
}
],
[
{
"x": 359.6776123046875,
"y": 148.3515167236328,
"time": 1702536452241
},
{
"x": 360.95751953125,
"y": 166.9501495361328,
"time": 1702536452258
},
{
"x": 361.6524658203125,
"y": 186.5827178955078,
"time": 1702536452270
},
{
"x": 362.3128662109375,
"y": 203.9375762939453,
"time": 1702536452288
},
{
"x": 362.9931640625,
"y": 218.0919647216797,
"time": 1702536452306
},
{
"x": 364.331787109375,
"y": 232.8119354248047,
"time": 1702536452320
},
{
"x": 364.984130859375,
"y": 247.2077178955078,
"time": 1702536452337
},
{
"x": 365.6175537109375,
"y": 260.9715118408203,
"time": 1702536452354
},
{
"x": 366.2884521484375,
"y": 275.6442413330078,
"time": 1702536452371
},
{
"x": 367.6258544921875,
"y": 289.0420379638672,
"time": 1702536452390
},
{
"x": 368.8919677734375,
"y": 301.1228790283203,
"time": 1702536452403
},
{
"x": 370.292724609375,
"y": 313.0811309814453,
"time": 1702536452421
},
{
"x": 372.9156494140625,
"y": 323.0240936279297,
"time": 1702536452438
},
{
"x": 374.9552001953125,
"y": 330.4568634033203,
"time": 1702536452455
},
{
"x": 377.5841064453125,
"y": 337.0873565673828,
"time": 1702536452471
},
{
"x": 378.9256591796875,
"y": 342.3609161376953,
"time": 1702536452488
},
{
"x": 380.2994384765625,
"y": 347.8545379638672,
"time": 1702536452505
},
{
"x": 381.62762451171875,
"y": 351.8480682373047,
"time": 1702536452520
},
{
"x": 382.308837890625,
"y": 354.4903106689453,
"time": 1702536452545
},
{
"x": 382.308837890625,
"y": 354.55714416503906,
"time": 1702536452556
}
],
[
{
"x": 586.8844604492188,
"y": 151.2805633544922,
"time": 1702536453141
},
{
"x": 585.511962890625,
"y": 165.02943420410156,
"time": 1702536453157
},
{
"x": 584.8742065429688,
"y": 183.56068420410156,
"time": 1702536453173
},
{
"x": 584.8742065429688,
"y": 201.2294158935547,
"time": 1702536453188
},
{
"x": 584.8742065429688,
"y": 215.3689422607422,
"time": 1702536453204
},
{
"x": 584.8742065429688,
"y": 230.58912658691406,
"time": 1702536453222
},
{
"x": 585.54052734375,
"y": 243.74464416503906,
"time": 1702536453239
},
{
"x": 586.8107299804688,
"y": 256.01344299316406,
"time": 1702536453256
},
{
"x": 587.5194091796875,
"y": 267.7810821533203,
"time": 1702536453271
},
{
"x": 588.8597412109375,
"y": 279.1865692138672,
"time": 1702536453288
},
{
"x": 590.2003784179688,
"y": 289.90699768066406,
"time": 1702536453304
},
{
"x": 591.52001953125,
"y": 301.1284942626953,
"time": 1702536453321
},
{
"x": 592.8444213867188,
"y": 311.0792694091797,
"time": 1702536453339
},
{
"x": 594.8500366210938,
"y": 320.5182342529297,
"time": 1702536453355
},
{
"x": 596.1693115234375,
"y": 328.3832244873047,
"time": 1702536453371
},
{
"x": 597.5363159179688,
"y": 336.58277893066406,
"time": 1702536453390
},
{
"x": 598.8732299804688,
"y": 343.2628936767578,
"time": 1702536453404
},
{
"x": 600.2008666992188,
"y": 348.56568908691406,
"time": 1702536453420
},
{
"x": 600.8662109375,
"y": 353.1544647216797,
"time": 1702536453438
},
{
"x": 600.8662109375,
"y": 356.4942169189453,
"time": 1702536453454
},
{
"x": 600.8662109375,
"y": 358.5269012451172,
"time": 1702536453472
},
{
"x": 600.8662109375,
"y": 358.5537872314453,
"time": 1702536453482
}
],
[
{
"x": 679.638916015625,
"y": 223.9750518798828,
"time": 1702536453873
},
{
"x": 698.049560546875,
"y": 220.69044494628906,
"time": 1702536453889
},
{
"x": 715.046142578125,
"y": 218.70338439941406,
"time": 1702536453912
},
{
"x": 731.1329956054688,
"y": 217.30799865722656,
"time": 1702536453922
},
{
"x": 743.7023315429688,
"y": 216.6453094482422,
"time": 1702536453939
},
{
"x": 755.0431518554688,
"y": 216.6720428466797,
"time": 1702536453957
},
{
"x": 764.043701171875,
"y": 216.6720428466797,
"time": 1702536453972
},
{
"x": 770.6744995117188,
"y": 216.6720428466797,
"time": 1702536453988
},
{
"x": 776.098388671875,
"y": 216.6720428466797,
"time": 1702536454005
},
{
"x": 780.066650390625,
"y": 216.6720428466797,
"time": 1702536454021
},
{
"x": 783.4193115234375,
"y": 216.6720428466797,
"time": 1702536454041
},
{
"x": 785.4308471679688,
"y": 216.6720428466797,
"time": 1702536454057
},
{
"x": 786.7714233398438,
"y": 216.6720428466797,
"time": 1702536454070
},
{
"x": 786.7733154296875,
"y": 216.6720428466797,
"time": 1702536454083
}
],
[
{
"x": 733.5531616210938,
"y": 316.62501525878906,
"time": 1702536454456
},
{
"x": 756.4210815429688,
"y": 315.2781524658203,
"time": 1702536454472
},
{
"x": 775.4960327148438,
"y": 315.2565460205078,
"time": 1702536454488
},
{
"x": 790.3649291992188,
"y": 317.1736297607422,
"time": 1702536454506
},
{
"x": 804.3414916992188,
"y": 319.8348846435547,
"time": 1702536454524
},
{
"x": 815.0912475585938,
"y": 323.1543731689453,
"time": 1702536454540
},
{
"x": 823.2052612304688,
"y": 325.8422393798828,
"time": 1702536454556
},
{
"x": 829.9567260742188,
"y": 328.5275115966797,
"time": 1702536454571
},
{
"x": 835.1862182617188,
"y": 329.8536834716797,
"time": 1702536454589
},
{
"x": 839.3932495117188,
"y": 330.5771026611328,
"time": 1702536454606
},
{
"x": 841.41259765625,
"y": 330.5771026611328,
"time": 1702536454618
}
],
[
{
"x": 783.1222534179688,
"y": 122.69233703613281,
"time": 1702536454959
},
{
"x": 807.8834838867188,
"y": 125.26026916503906,
"time": 1702536454974
},
{
"x": 833.0866088867188,
"y": 128.6871337890625,
"time": 1702536454989
},
{
"x": 854.1300659179688,
"y": 132.88661193847656,
"time": 1702536455006
},
{
"x": 869.70361328125,
"y": 138.1724853515625,
"time": 1702536455021
},
{
"x": 882.982666015625,
"y": 144.5460968017578,
"time": 1702536455042
},
{
"x": 895.916748046875,
"y": 152.63514709472656,
"time": 1702536455056
},
{
"x": 906.02685546875,
"y": 160.70143127441406,
"time": 1702536455071
},
{
"x": 916.085693359375,
"y": 170.0878448486328,
"time": 1702536455090
},
{
"x": 925.06689453125,
"y": 182.9326934814453,
"time": 1702536455105
},
{
"x": 932.54638671875,
"y": 195.7455291748047,
"time": 1702536455121
},
{
"x": 937.921142578125,
"y": 209.0699920654297,
"time": 1702536455139
},
{
"x": 941.3206787109375,
"y": 221.79115295410156,
"time": 1702536455159
},
{
"x": 943.363037109375,
"y": 235.3358612060547,
"time": 1702536455171
},
{
"x": 943.3616943359375,
"y": 248.3235626220703,
"time": 1702536455187
},
{
"x": 942.05517578125,
"y": 261.0668182373047,
"time": 1702536455205
},
{
"x": 938.763427734375,
"y": 275.0695037841797,
"time": 1702536455221
},
{
"x": 933.5555419921875,
"y": 287.5211639404297,
"time": 1702536455238
},
{
"x": 928.054443359375,
"y": 298.5670928955078,
"time": 1702536455255
},
{
"x": 921.485107421875,
"y": 307.7720489501953,
"time": 1702536455271
},
{
"x": 913.521728515625,
"y": 316.4197235107422,
"time": 1702536455288
},
{
"x": 905.4949951171875,
"y": 325.1339569091797,
"time": 1702536455305
},
{
"x": 896.376953125,
"y": 333.6233673095703,
"time": 1702536455321
},
{
"x": 885.5185546875,
"y": 342.4713592529297,
"time": 1702536455339
},
{
"x": 874.749755859375,
"y": 350.5452117919922,
"time": 1702536455356
},
{
"x": 863.4857177734375,
"y": 359.8206329345703,
"time": 1702536455370
},
{
"x": 852.8712768554688,
"y": 367.1151580810547,
"time": 1702536455391
},
{
"x": 841.2532958984375,
"y": 374.6466522216797,
"time": 1702536455404
},
{
"x": 829.65380859375,
"y": 381.0709991455078,
"time": 1702536455421
},
{
"x": 817.5537109375,
"y": 387.79103088378906,
"time": 1702536455437
},
{
"x": 805.4242553710938,
"y": 393.8605499267578,
"time": 1702536455454
},
{
"x": 793.5094604492188,
"y": 399.15431213378906,
"time": 1702536455472
},
{
"x": 782.9738159179688,
"y": 403.1088409423828,
"time": 1702536455489
},
{
"x": 772.8463745117188,
"y": 406.4949493408203,
"time": 1702536455504
},
{
"x": 764.2553100585938,
"y": 409.1325225830078,
"time": 1702536455522
},
{
"x": 756.42041015625,
"y": 411.1269073486328,
"time": 1702536455540
},
{
"x": 749.0506591796875,
"y": 413.7377471923828,
"time": 1702536455557
},
{
"x": 742.9489135742188,
"y": 416.42872619628906,
"time": 1702536455572
},
{
"x": 737.5338745117188,
"y": 418.4693145751953,
"time": 1702536455588
},
{
"x": 733.5607299804688,
"y": 420.4709014892578,
"time": 1702536455605
},
{
"x": 730.177734375,
"y": 422.4792022705078,
"time": 1702536455622
},
{
"x": 728.816650390625,
"y": 424.4844512939453,
"time": 1702536455638
},
{
"x": 728.80224609375,
"y": 427.1183624267578,
"time": 1702536455655
},
{
"x": 732.04443359375,
"y": 429.1317901611328,
"time": 1702536455671
},
{
"x": 739.4037475585938,
"y": 431.8090362548828,
"time": 1702536455688
},
{
"x": 752.61962890625,
"y": 435.11402893066406,
"time": 1702536455712
},
{
"x": 767.9056396484375,
"y": 437.7860870361328,
"time": 1702536455727
},
{
"x": 784.0560302734375,
"y": 439.1490020751953,
"time": 1702536455738
},
{
"x": 800.2537841796875,
"y": 441.17530822753906,
"time": 1702536455755
},
{
"x": 820.0858154296875,
"y": 443.8155059814453,
"time": 1702536455772
},
{
"x": 837.1102905273438,
"y": 445.79103088378906,
"time": 1702536455787
},
{
"x": 852.6246948242188,
"y": 447.1361846923828,
"time": 1702536455803
},
{
"x": 865.9520263671875,
"y": 449.1322784423828,
"time": 1702536455820
},
{
"x": 876.6416015625,
"y": 451.1309356689453,
"time": 1702536455844
},
{
"x": 886.6123046875,
"y": 453.1099395751953,
"time": 1702536455855
},
{
"x": 894.085205078125,
"y": 454.4803009033203,
"time": 1702536455871
},
{
"x": 899.9359130859375,
"y": 455.77760314941406,
"time": 1702536455888
},
{
"x": 904.62646484375,
"y": 458.4120635986328,
"time": 1702536455904
},
{
"x": 908.64892578125,
"y": 460.4477081298828,
"time": 1702536455920
},
{
"x": 908.71240234375,
"y": 460.4689483642578,
"time": 1702536455930
}
]
]
// state.nowPoints = props.pointData
initCanvasByVideo()
state.allTime = Math.ceil((state.nowPoints[state.nowPoints.length - 1][state.nowPoints[state.nowPoints.length - 1].length - 1].time - state.nowPoints[0][0].time) / 1000) // 最后一个坐标的时间 - 第一个坐标的时间
})
// 快速播放
const playRocket = async () => {
// 把所有状态清零
state.isPlayVideo = false
state.isStartVideo = false
state.indexStep = 0
state.indexPoint = 0
if (state.nowTimer) {
clearInterval(state.nowTimer)
}
if (state.drawStepTimer) {
clearTimeout(state.drawStepTimer)
}
if (state.drawLineTimer) {
clearTimeout(state.drawLineTimer)
}
state.nowTime = 0
if (state.isRocket) {
state.isRocket = false
return
}
state.isPlayVideo = false
state.isRocket = true
state.ctxByVideo.strokeStyle = props.color
state.ctxByVideo.translate(0.5, 0.5) // 抗锯齿(好像没得卵用)
state.ctxByVideo.antialias = 'smooth' // 抗锯齿(好像没得卵用)
state.ctxByVideo.imageSmoothingEnabled = true // 抗锯齿(好像没得卵用)
state.ctxByVideo.lineJoin = 'round' // 设置圆角
state.ctxByVideo.lineWidth = props.lineWidth // 设置粗细
state.ctxByVideo.shadowBlur = 1;
state.ctxByVideo.shadowColor = props.color;
state.ctxByVideo.clearRect(0, 0, state.myCanvasByVideo.width, state.myCanvasByVideo.height); // 清空
if (state.drawStepTimer) {
clearTimeout(state.drawStepTimer)
}
if (state.drawLineTimer) {
clearTimeout(state.drawLineTimer)
}
for (let j = 0; j < state.nowPoints.length; j++) {
state.indexStep = j
state.ctxByVideo.beginPath();
let timeout = 0
if (j !== 0) {
if (state.nowPoints[j].length && state.nowPoints[j - 1].length) {
timeout = state.nowPoints[j][0].time - state.nowPoints[j - 1][state.nowPoints[j - 1].length - 1].time
}
}
await drawStepByRocket(state.nowPoints[j], 50)
}
state.isRocket = false
}
//答题笔迹
const playVideo = async () => {
if (state.isRocket) {
state.isPlayVideo = false
clearInterval(state.nowTimer)
clearTimeout(state.drawLineTimer)
clearTimeout(state.drawStepTimer)
state.myCanvasByVideo.width = state.myCanvasByVideo.width // 清空
state.canvasHistory = null
// 清空操作
state.nowTime = 0
state.isPlayVideo = false
state.isStartVideo = false
state.indexStep = 0
state.indexPoint = 0
clearInterval(state.nowTimer)
state.canvasHistory = null
if (state.nowTimer) {
clearInterval(state.nowTimer)
}
if (state.drawStepTimer) {
clearTimeout(state.drawStepTimer)
}
if (state.drawLineTimer) {
clearTimeout(state.drawLineTimer)
}
}
state.isRocket = false
state.isPlayVideo = true
state.isStartVideo = true
state.ctxByVideo.strokeStyle = props.color
state.ctxByVideo.translate(0.5, 0.5) // 抗锯齿(好像没得卵用)
state.ctxByVideo.antialias = 'smooth' // 抗锯齿(好像没得卵用)
state.ctxByVideo.imageSmoothingEnabled = true // 抗锯齿(好像没得卵用)
state.ctxByVideo.lineJoin = 'round' // 设置圆角
state.ctxByVideo.lineWidth = props.lineWidth // 设置粗细
state.ctxByVideo.shadowBlur = 1;
state.ctxByVideo.shadowColor = props.color;
state.ctxByVideo.clearRect(0, 0, state.myCanvasByVideo.width, state.myCanvasByVideo.height); // 清空
state.nowTimer = setInterval(()=> {
if (state.nowTime < state.allTime) {
state.nowTime++
}
}, 1000 / state.nowSpeed)
for (let j = 0; j < state.nowPoints.length; j++) {
state.indexStep = j
state.ctxByVideo.beginPath();
let timeout = 0
if (j !== 0) {
if (state.nowPoints[j].length && state.nowPoints[j - 1].length) {
timeout = state.nowPoints[j][0].time - state.nowPoints[j - 1][state.nowPoints[j - 1].length - 1].time
}
}
await drawStep(state.nowPoints[j], props.lineWidth / state.nowSpeed)
}
state.nowTime = state.allTime
state.isPlayVideo = false
clearInterval(state.nowTimer)
}
// 暂停播放
const pauseVideo = () => {
state.isPlayVideo = false
clearInterval(state.nowTimer)
clearTimeout(state.drawLineTimer)
clearTimeout(state.drawStepTimer)
state.ctxByVideo.stroke()
}
// 继续播放
const continueVideo = async () => {
if (state.nowTime === state.allTime) { // 播放完了重新播放
state.nowTime = 0
playVideo()
return
}
state.isPlayVideo = true
let startIndex = JSON.parse(JSON.stringify(state.indexStep))
state.nowTimer = setInterval(()=> {
if (state.nowTime < state.allTime) {
state.nowTime++
}
}, 1000 / state.nowSpeed)
console.log('从这开始', state.indexStep, state.nowPoints)
// state.ctx.moveTo(0, 0)
// state.ctx.lineTo(500, 500)
// state.ctx.stroke();
// state.ctx.closePath();
for (let j = startIndex; j < state.nowPoints.length; j++) {
state.indexStep = j
let timeout = 0
if (j !== 0) {
if (state.nowPoints[j].length && state.nowPoints[j - 1].length) {
timeout = state.nowPoints[j][0].time - state.nowPoints[j - 1][state.nowPoints[j - 1].length - 1].time
}
}
await drawStep(state.nowPoints[j], timeout / state.nowSpeed)
}
state.nowTime = state.allTime
clearInterval(state.nowTimer)
state.isPlayVideo = false
}
// 改变进度条(根据当前时间获取)
const changeVideoSilder = () => {
console.log('改变了')
clearInterval(state.nowTimer)
clearTimeout(state.drawLineTimer)
clearTimeout(state.drawStepTimer)
state.ctxByVideo.clearRect(0, 0, state.myCanvasByVideo.width, state.myCanvasByVideo.height); // 清空
state.ctxByVideo.strokeStyle = props.color
state.ctxByVideo.translate(0.5, 0.5) // 抗锯齿(好像没得卵用)
state.ctxByVideo.antialias = 'smooth' // 抗锯齿(好像没得卵用)
state.ctxByVideo.imageSmoothingEnabled = true // 抗锯齿(好像没得卵用)
state.ctxByVideo.lineJoin = 'round' // 设置圆角
state.ctxByVideo.lineWidth = props.lineWidth // 设置粗细
state.ctxByVideo.shadowBlur = 1;
state.ctxByVideo.shadowColor = props.color;
let allTime = 0
// 直接把进度条当前定位之前的画出来不加任何延时
here: for (let i = 0;i < state.nowPoints.length;i++) {
state.ctxByVideo.beginPath();
if (i) {
allTime += (state.nowPoints[i][0].time - state.nowPoints[i - 1][state.nowPoints[i - 1].length - 1].time)
console.log('1级时间', allTime, i)
}
for (let j = 0;j < state.nowPoints[i].length;j++) {
if (j) {
allTime += (state.nowPoints[i][j].time - state.nowPoints[i][j - 1].time)
console.log('2级时间', allTime, i, j)
}
if (j !== state.nowPoints[i].length - 1) {
state.ctxByVideo.moveTo(state.nowPoints[i][j].x / state.gdbl, state.nowPoints[i][j].y / state.gdbl)
state.ctxByVideo.lineTo(state.nowPoints[i][j+1].x / state.gdbl, state.nowPoints[i][j+1].y / state.gdbl)
state.ctxByVideo.stroke();
state.ctxByVideo.closePath();
}
if (allTime >= (state.nowTime * 1000)) {
state.indexStep = i
state.indexPoint = j
break here
}
}
}
if (state.isPlayVideo) { // 如果是播放状态 则继续播放
continueVideo()
}
}
const drawStep = (data:any, time:any) => {
return new Promise((resolve) => {
state.drawStepTimer = setTimeout(async () => {
let startIndex = JSON.parse(JSON.stringify(state.indexPoint))
for (let i = startIndex; i < data.length - 1; i++) {
state.indexPoint = i
let timeLine = data[i + 1].time - data[i].time
if (timeLine) { // 点阵笔有很多点位不同但时间相同的点 不做异步处理
await drawLine(data[i].x / state.gdbl, data[i].y / state.gdbl, data[i + 1].x / state.gdbl, data[i + 1].y / state.gdbl, (data[i].x + data[i + 1].x) / 2 / state.gdbl, (data[i].y + data[i + 1].y) / 2 / state.gdbl, timeLine / state.nowSpeed)
} else {
state.ctxByVideo.moveTo(data[i].x / state.gdbl, data[i].y / state.gdbl)
state.ctxByVideo.lineTo(data[i + 1].x / state.gdbl, data[i + 1].y / state.gdbl)
state.ctxByVideo.stroke()
state.ctxByVideo.closePath()
}
}
state.indexPoint = 0
state.ctxByVideo.stroke();
resolve()
}, time)
})
}
// 相同速度播放
const drawStepByRocket = (data:any, time:any) => {
return new Promise((resolve) => {
state.drawStepTimer = setTimeout(async () => {
let startIndex = JSON.parse(JSON.stringify(state.indexPoint))
for (let i = startIndex; i < data.length - 1; i++) {
state.indexPoint = i
let timeLine = data[i + 1].time - data[i].time
if (timeLine) { // 点阵笔有很多点位不同但时间相同的点 不做异步处理
await drawLine(data[i].x / state.gdbl, data[i].y / state.gdbl, data[i + 1].x / state.gdbl, data[i + 1].y / state.gdbl, (data[i].x + data[i + 1].x) / 2 / state.gdbl, (data[i].y + data[i + 1].y) / 2 / state.gdbl, 5)
} else {
state.ctxByVideo.moveTo(data[i].x / state.gdbl, data[i].y / state.gdbl)
state.ctxByVideo.lineTo(data[i + 1].x / state.gdbl, data[i + 1].y / state.gdbl)
state.ctxByVideo.stroke()
state.ctxByVideo.closePath()
}
}
state.indexPoint = 0
state.ctxByVideo.stroke();
resolve()
}, time)
})
}
const drawLine = (x1:any, y1:any, x2:any, y2:any, controlX:any, controlY:any, time:any) => {
return new Promise((resolve) => {
state.drawLineTimer = setTimeout(() => {
state.ctxByVideo.moveTo(x1, y1)
state.ctxByVideo.lineTo(x2, y2)
// state.ctx.quadraticCurveTo(controlX, controlY, x2, y2)
state.ctxByVideo.stroke();
state.ctxByVideo.closePath();
resolve()
}, time)
})
}
// 改变播放速度
const changeSpeed = (speed:any) => {
state.nowSpeed = speed
state.isShowSpeedBox = false
if (state.nowTimer) {
clearInterval(state.nowTimer)
if (state.isPlayVideo) {
state.nowTimer = setInterval(()=> {
if (state.nowTime < state.allTime) {
state.nowTime++
}
}, 1000 / state.nowSpeed)
}
}
}
const getFormatTime = (second:any) => {
return formatSecond(second)
}
const initCanvasByVideo = () => {
state.myCanvasByVideo = document.getElementById("myCanvasByVideo");
state.ctxByVideo = state.myCanvasByVideo.getContext("2d");
}
const formatSecond = (allSecond:any) => {
let minute = 0
let second = 0
second = allSecond % 60
if (second < 10) {
state.second = '0' + second
}
minute = Math.trunc(allSecond / 60)
if (minute < 10) {
state.minute = '0' + minute
}
return state.minute + ':' + state.second
}
</script>
<style lang="scss" scoped>
.canvas-video{
width: 100%;
height: 100%;
.myCanvas{
background: black;
}
.btnDiv{
display: flex;
justify-content: center;
margin-top: 5px;
.playback{
display: flex;
justify-content: space-between;
color: #ffffff;
text-align: center;
padding: 0 15px;
border-radius: 4px;
background: #00B386;
height: 28px;
line-height: 28px;
cursor: pointer;
img{
width: 10px;
height: 14px;
margin-top: 7px;
margin-left: 12px;
}
}
.rocket{
margin-top: 3px;
margin-left: 10px;
cursor: pointer;
}
.mySlider{
width: 500px;
margin-left: 20px;
::v-deep .el-slider__bar{
border-radius: 17px;
background: #00B386;
}
::v-deep .el-slider__button{
border: 1px solid #00B386;
}
::v-deep .el-slider__runway{
border-radius: 17px;
background: #444;
}
}
.myTime{
margin-left: 14px;
color: #A9A9A9;
line-height: 35px;
}
.mySpeed{
cursor: pointer;
position: relative;
border-radius: 2px;
background: #EF8714;
width: 60px;
height: 20px;
line-height: 20px;
text-align: center;
color: #ffffff;
margin-left: 20px;
margin-top: 5px;
.speedList{
position: absolute;
bottom: 20px;
border-radius: 2px;
background: #232322;
padding: 10px 0;
width: 60px;
.speedItem{
width: 100%;
text-align: center;
margin-bottom: 10px;
color: #FFFFFF;
}
.speedItem.active{
color: #EF8714;
font-size: 14px;
}
.speedItem:last-child{
margin-bottom: 0px;
}
z-index: 2000;
}
}
.slider-warpper {
width: 320px;
height: 16px;
position: absolute;
left: 280px;
// background: #ef9e00 !important;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.slider-content {
width: 304px;
height: 16px;
background: #2D2D2D;
border: 1px solid rgba(255, 186, 33, 0.16);
border-radius: 22px;
position: relative;
.slider {
border-radius: 22px;
position: absolute;
left: 0;
top: 0;
width: 30px;
height: 16px;
background: #FFBA21;
}
}
.persent {
margin-left: 10px;
color: #FFBA21;
font-size: 18px;
font-weight: 600;
letter-spacing: 1.26px;
}
}
}
}
</style>
?
(1)安装 canvasvideo-vue
npm install canvasvideo-vue --save
(2)main.js引入
import { createApp } from 'vue'
import App from './App.vue'
import canvasVideo from "canvasvideo-vue"
import "../node_modules/canvasvideo-vue/canvasVideo-vue.css"
const app = createApp(App)
app.use(canvasVideo)
.mount("#app")
(3)页面调用
<canvas-video :width="1220" :height="500" :backgroundColor="'blue'" :color="'red'" :lineWidth="1" :pointData="backPlayList" />
<canvas-video :width="1220" :height="500" :backgroundColor="'blue'" :color="'red'" :lineWidth="1" :pointData="backPlayList" />
<script setup lang="ts">
import { ref } from 'vue'
import CanvasVideo from "./components/CanvasVideo.vue"
const backPlayList = ref<any[]>([])
// backPlayList 去接收处理后台返回的数据
</script>
? ? ? 希望我的愚见能够帮助你哦~,若有不足之处,还望指出,你们有更好的解决方法,欢迎大家在评论区下方留言支持,大家一起相互学习参考呀~