• 使用canvas实现时间轴上滑块的各种常用操作(仅供参考)


    一、简介

    使用canvas,模拟绘制时间轴区域,有时间刻度标尺,时间轴区域上会有多行,每行都有一个滑块。

    1、时间刻度标尺可以拖动,会自动对齐整数点秒数,最小步数为0.1秒。

    2、滑块可以自由拖动,当滑块处于选中状态时,左右两边会出现可拖动的按钮,用于拉伸宽度。

    3、滑块之间可以自由拖动交换位置。

    4、滑块与滑块之间对齐时会出现对齐虚线,滑块与刻度标尺对齐时,刻度标尺会变色用于提醒用户此时已对齐。

    5、当滑块拉伸到最右侧区域时,右侧空间不足时,会自动增加右侧空间区域。而当做右侧滑块的位置往左移动时,如果出现右侧空间区域过大,则会自动减少右侧空间区域,始终保持右侧空间留白区域是预设的宽度。

    07c925f5a8664149b569f64b75e419cd.png

     

    二、案例代码

    1. <template>
    2. <div class="main-container" ref="tWrap" @scroll="tWrapScroll($event)">
    3. <canvas id="tl-canvas" ref="tl-canvas" width="700" height="300" @mousedown.stop.prevent="cMouseDown($event)"
    4. @mousemove.stop.prevent="cMouseMove($event)" @mouseup.stop.prevent="cMouseUp($event)"
    5. @mouseleave.stop.prevent="cMouseUp($event)">canvas>
    6. <div class="hidden-box" :style="{
    7. width: cMaxWidth + 'px',
    8. height: cMaxHeight + 'px'
    9. }">div>
    10. div>
    11. template>
    12. <script>
    13. export default {
    14. data() {
    15. return {
    16. tWrapScrollTop: 0,
    17. tWrapScrollLeft: 0,
    18. tWrapEle: null,
    19. tCanvas: null,
    20. ctx: null,
    21. minY: 50,
    22. maxY: 500,
    23. minX: 10, // 可拖动的x轴最左侧
    24. maxX: 700, // 可拖动的x轴最右侧
    25. rDistant: 300, // 画布右侧留白区域距离
    26. cWidth: 700, // 画布的宽度
    27. cHeight: 300, // 画布的高度
    28. cMaxWidth: 1000, // 实际画布需要的宽度
    29. cMaxHeight: 500, // 实际画布需要的高度
    30. btnWidth: 20, // 左右按钮宽度
    31. lineHeight: 50, // 滑块高度
    32. moveItem: null, // 当前移动的滑块
    33. items: [
    34. {
    35. zIndex: 1,
    36. id: 1,
    37. active: false,
    38. tTop: 0,
    39. tLeft: 10,
    40. tWidth: 100,
    41. tHeight: 50
    42. },
    43. {
    44. zIndex: 2,
    45. id: 2,
    46. active: false,
    47. tTop: 0,
    48. tLeft: 10,
    49. tWidth: 150,
    50. tHeight: 50
    51. },
    52. {
    53. zIndex: 3,
    54. id: 3,
    55. active: false,
    56. tTop: 0,
    57. tLeft: 10,
    58. tWidth: 200,
    59. tHeight: 50
    60. },
    61. ],
    62. bcMoveAbled: false, // 刻度尺可移动的标识
    63. moveAbled: false, // 滑块可移动的标识
    64. dragLeftAbled: false, // 滑块可左拖的标识
    65. dragRightAbled: false, // 滑块可右拖的标识
    66. oldMouseX: 0,
    67. oldMouseY: 0,
    68. alignLine: null, // 对齐虚线对象
    69. alignStaff: false, // 刻度尺对齐标识
    70. currentTime: 10, // 刻度尺当前对齐的时间
    71. }
    72. },
    73. mounted() {
    74. this.$nextTick(() => {
    75. this.tCanvas = document.getElementById('tl-canvas')
    76. this.ctx = this.tCanvas.getContext('2d')
    77. let twrap = this.$refs['tWrap'].getBoundingClientRect()
    78. this.tWrapEle = twrap
    79. this.updateCanvasDom()
    80. this.doDrawTimeLine()
    81. })
    82. },
    83. beforeUnmount() {
    84. },
    85. methods: {
    86. /**
    87. * 监听滚动事件
    88. * @param {*} e
    89. */
    90. tWrapScroll(e) {
    91. this.tWrapScrollTop = this.$refs['tWrap'].scrollTop
    92. this.tWrapScrollLeft = this.$refs['tWrap'].scrollLeft
    93. // console.log(this.$refs['tWrap'].scrollTop)
    94. },
    95. /**
    96. * 判断点是否在多边形内
    97. * @param {*} p
    98. * @param {*} ptPolygon
    99. */
    100. isInPolygon(p, ptPolygon) {
    101. let ncross = 0;
    102. for (let i = 0; i < ptPolygon.length; i++) {
    103. let p1 = ptPolygon[i];
    104. let p2 = ptPolygon[(i + 1) % ptPolygon.length]; // 相邻两条边p1,p2
    105. if (p1.y == p2.y) {
    106. continue;
    107. }
    108. if (p.y < Math.min(p1.y, p2.y)) {
    109. continue;
    110. }
    111. if (p.y >= Math.max(p1.y, p2.y)) {
    112. continue;
    113. }
    114. let x = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
    115. if (x > p.x) {
    116. ncross++; // 只统计单边交点
    117. }
    118. }
    119. return (ncross % 2 == 1);
    120. },
    121. /**
    122. * 判断是否出现对齐虚线
    123. */
    124. showAlignLine(item) {
    125. let _n = 3
    126. // 判断是否对齐刻度尺
    127. let _bcX = (this.currentTime*10+this.minX)
    128. // 移动对齐标尺
    129. if(this.moveAbled) {
    130. if(Math.abs(item.tLeft - _bcX) <= _n) {
    131. this.alignStaff = true
    132. return {
    133. left: _bcX
    134. }
    135. } else if(Math.abs(item.tLeft+item.tWidth - _bcX) <= _n) {
    136. this.alignStaff = true
    137. return {
    138. left: _bcX - item.tWidth
    139. }
    140. } else {
    141. this.alignStaff = false
    142. }
    143. }
    144. // 左拖对齐标尺
    145. else if(this.dragLeftAbled) {
    146. if(Math.abs(item.tLeft - _bcX) <= _n) {
    147. this.alignStaff = true
    148. return {
    149. n: item.tLeft - _bcX,
    150. left: _bcX
    151. }
    152. } else {
    153. this.alignStaff = false
    154. }
    155. }
    156. // 右拖对齐标尺
    157. else if(this.dragRightAbled) {
    158. if(Math.abs(item.tLeft + item.tWidth - _bcX) <= _n) {
    159. this.alignStaff = true
    160. return {
    161. n: _bcX - (item.tLeft + item.tWidth)
    162. }
    163. } else {
    164. this.alignStaff = false
    165. }
    166. }
    167. // 判断滑块之间的对齐
    168. for(let i=0; i < this.items.length; i++) {
    169. // 移动
    170. if(this.moveAbled && i !== this.moveItem.index) {
    171. if(Math.abs(item.tLeft - this.items[i].tLeft) <= _n) {
    172. this.alignLine = {
    173. top: 0,
    174. left: this.items[i].tLeft,
    175. height: this.cHeight
    176. }
    177. return {
    178. left: this.items[i].tLeft
    179. }
    180. break
    181. }
    182. else if(Math.abs(item.tLeft+item.tWidth - this.items[i].tLeft) <= _n ) {
    183. this.alignLine = {
    184. top: 0,
    185. left: this.items[i].tLeft,
    186. height: this.cHeight
    187. }
    188. return {
    189. left: this.items[i].tLeft - item.tWidth
    190. }
    191. break
    192. }
    193. else if(Math.abs(this.items[i].tLeft+this.items[i].tWidth - item.tLeft) <= _n) {
    194. this.alignLine = {
    195. top: 0,
    196. left: this.items[i].tLeft+this.items[i].tWidth,
    197. height: this.cHeight
    198. }
    199. return {
    200. left: this.items[i].tLeft+this.items[i].tWidth
    201. }
    202. break
    203. }
    204. else if(Math.abs(item.tLeft+item.tWidth - (this.items[i].tLeft+this.items[i].tWidth)) <= _n) {
    205. this.alignLine = {
    206. top: 0,
    207. left: this.items[i].tLeft+this.items[i].tWidth,
    208. height: this.cHeight
    209. }
    210. return {
    211. left: this.items[i].tLeft+this.items[i].tWidth - item.tWidth
    212. }
    213. break
    214. }
    215. }
    216. // 左拖
    217. else if(this.dragLeftAbled && i !== this.moveItem.index) {
    218. if(Math.abs(item.tLeft - this.items[i].tLeft) <= _n) {
    219. this.alignLine = {
    220. top: 0,
    221. left: this.items[i].tLeft,
    222. height: this.cHeight
    223. }
    224. return {
    225. n: item.tLeft - this.items[i].tLeft,
    226. left: this.items[i].tLeft
    227. }
    228. break
    229. }
    230. else if(Math.abs(this.items[i].tLeft+this.items[i].tWidth - item.tLeft) <= _n) {
    231. this.alignLine = {
    232. top: 0,
    233. left: this.items[i].tLeft+this.items[i].tWidth,
    234. height: this.cHeight
    235. }
    236. return {
    237. n: item.tLeft - (this.items[i].tLeft+this.items[i].tWidth),
    238. left: this.items[i].tLeft+this.items[i].tWidth
    239. }
    240. break
    241. }
    242. }
    243. // 右拖
    244. else if(this.dragRightAbled && i !== this.moveItem.index) {
    245. if(Math.abs(item.tLeft+item.tWidth - (this.items[i].tLeft+this.items[i].tWidth)) <= _n) {
    246. this.alignLine = {
    247. top: 0,
    248. left: this.items[i].tLeft+this.items[i].tWidth,
    249. height: this.cHeight
    250. }
    251. return {
    252. n: this.items[i].tLeft+this.items[i].tWidth - (item.tLeft+item.tWidth),
    253. // left: this.items[i].tLeft+this.items[i].tWidth - item.tWidth
    254. }
    255. break
    256. }
    257. else if(Math.abs(item.tLeft+item.tWidth - this.items[i].tLeft) <= _n) {
    258. this.alignLine = {
    259. top: 0,
    260. left: this.items[i].tLeft,
    261. height: this.cHeight
    262. }
    263. return {
    264. n: this.items[i].tLeft - (item.tLeft+item.tWidth),
    265. // left: this.items[i].tLeft - item.tWidth
    266. }
    267. break
    268. }
    269. }
    270. }
    271. // 没有对齐
    272. this.alignLine = null
    273. return false
    274. },
    275. /**
    276. * 检测当前滑块的最大长度和数量,随时更新画布的最大宽度和高度
    277. */
    278. updateCanvasDom() {
    279. let maxWidth = 0
    280. // 按层级排序
    281. this.items.sort((a, b) => b.zIndex - a.zIndex)
    282. for (let i = 0; i < this.items.length; i++) {
    283. // 获取最大宽度
    284. maxWidth = this.items[i].tLeft + this.items[i].tWidth > maxWidth ? this.items[i].tLeft + this.items[i].tWidth : maxWidth
    285. // 重新更新y坐标
    286. this.items[i].tTop = 5 + this.lineHeight * i + 5 * i + this.minY
    287. }
    288. this.items = JSON.parse(JSON.stringify(this.items))
    289. // 留白区域大于预设
    290. if (this.cMaxWidth - maxWidth > this.rDistant && this.cMaxWidth - this.rDistant > this.cWidth) {
    291. this.cMaxWidth = maxWidth + this.rDistant
    292. this.maxX = this.cMaxWidth - this.rDistant
    293. }
    294. // 留白区域小于预设
    295. if (this.cMaxWidth - maxWidth < this.rDistant) {
    296. this.cMaxWidth += (this.rDistant - (this.cMaxWidth - maxWidth))
    297. this.maxX = this.cMaxWidth - this.rDistant
    298. }
    299. this.cMaxHeight = this.items.length * 55 > this.maxY ? this.items.length * 55 : this.maxY
    300. },
    301. /**
    302. * 鼠标点击
    303. */
    304. cMouseDown(e) {
    305. // 判断是否点击到标尺
    306. let _bcX = this.minX + this.currentTime*10
    307. let _mX = e.clientX - this.tWrapEle.left + this.tWrapScrollLeft
    308. if(_mX >= _bcX - 2 && _mX <= _bcX+2) {
    309. console.log('点击标尺', this.currentTime)
    310. this.tCanvas.style.cursor = 'grab'
    311. this.bcMoveAbled = true
    312. this.oldMouseX = e.clientX
    313. this.oldMouseY = e.clientY
    314. return
    315. }
    316. // 判断是否点击到滑块
    317. for (let i = 0; i < this.items.length; i++) {
    318. let item = JSON.parse(JSON.stringify(this.items[i]))
    319. item.tLeft = item.tLeft - this.tWrapScrollLeft
    320. item.tTop = item.tTop - this.tWrapScrollTop
    321. // 判断鼠标坐标是否在滑块上
    322. if (this.isInPolygon({
    323. x: e.clientX - this.tWrapEle.left,
    324. y: e.clientY - this.tWrapEle.top
    325. }, [
    326. {
    327. x: item.tLeft,
    328. y: item.tTop
    329. },
    330. {
    331. x: item.tLeft + item.tWidth,
    332. y: item.tTop
    333. },
    334. {
    335. x: item.tLeft + item.tWidth,
    336. y: item.tTop + item.tHeight
    337. },
    338. {
    339. x: item.tLeft,
    340. y: item.tTop + item.tHeight
    341. }
    342. ])) {
    343. if (item.active) {
    344. // 判断是否在右按钮上
    345. if (this.isInPolygon({
    346. x: e.clientX - this.tWrapEle.left,
    347. y: e.clientY - this.tWrapEle.top
    348. }, [
    349. {
    350. x: item.tLeft + item.tWidth - this.btnWidth,
    351. y: item.tTop
    352. },
    353. {
    354. x: item.tLeft + item.tWidth,
    355. y: item.tTop
    356. },
    357. {
    358. x: item.tLeft + item.tWidth,
    359. y: item.tTop + item.tHeight
    360. },
    361. {
    362. x: item.tLeft + item.tWidth - this.btnWidth,
    363. y: item.tTop + item.tHeight
    364. }
    365. ])) {
    366. this.dragRightAbled = true
    367. this.tCanvas.style.cursor = 'e-resize'
    368. }
    369. // 判断是否在左按钮上
    370. else if (this.isInPolygon({
    371. x: e.clientX - this.tWrapEle.left,
    372. y: e.clientY - this.tWrapEle.top
    373. }, [
    374. {
    375. x: item.tLeft,
    376. y: item.tTop
    377. },
    378. {
    379. x: item.tLeft + this.btnWidth,
    380. y: item.tTop
    381. },
    382. {
    383. x: item.tLeft + this.btnWidth,
    384. y: item.tTop + item.tHeight
    385. },
    386. {
    387. x: item.tLeft,
    388. y: item.tTop + item.tHeight
    389. }
    390. ])) {
    391. this.dragLeftAbled = true
    392. this.tCanvas.style.cursor = 'w-resize'
    393. }
    394. // 在滑块上
    395. else {
    396. this.moveAbled = true
    397. this.tCanvas.style.cursor = 'grab'
    398. }
    399. } else {
    400. for (let i = 0; i < this.items.length; i++) {
    401. this.items[i].active = false
    402. }
    403. // 在滑块上
    404. this.tCanvas.style.cursor = 'grab'
    405. this.moveAbled = true
    406. this.items[i].active = true
    407. }
    408. // 保存移动的item
    409. this.moveItem = JSON.parse(JSON.stringify(this.items[i]))
    410. this.moveItem.index = i
    411. console.log('点击', this.moveItem)
    412. this.oldMouseX = e.clientX
    413. this.oldMouseY = e.clientY
    414. break
    415. } else {
    416. this.tCanvas.style.cursor = 'auto'
    417. this.items[i].active = false
    418. this.moveAbled = false
    419. this.dragLeftAbled = false
    420. this.dragRightAbled = false
    421. this.oldMouseX = 0
    422. this.oldMouseY = 0
    423. }
    424. }
    425. },
    426. /**
    427. * 鼠标移动
    428. */
    429. cMouseMove(e) {
    430. // 刻度尺
    431. if(this.bcMoveAbled) {
    432. let _oldMouseX = e.clientX
    433. let _d = _oldMouseX - this.oldMouseX
    434. this.oldMouseX = _oldMouseX
    435. let _n = 0.3
    436. let _time = this.currentTime + _d/10
    437. // 判断是否越界了
    438. if(_time < 0) {
    439. _time = 0
    440. }
    441. else if(_time * 10 + this.minX > this.maxX) {
    442. console.log('xxxx', this.maxX)
    443. _time = (this.maxX - this.minX)/10
    444. }
    445. // 判断是否移动到整数秒位置
    446. else if(Math.abs(Math.round(_time) - _time) <= _n) {
    447. this.oldMouseX += (Math.round(_time) - _time)*10
    448. _time = Math.round(_time)
    449. this.alignStaff = true
    450. }
    451. else {
    452. this.alignStaff = false
    453. }
    454. this.currentTime = _time
    455. console.log(this.currentTime)
    456. }
    457. else if (this.moveItem) {
    458. // 移动中
    459. if (this.moveAbled) {
    460. let item = JSON.parse(JSON.stringify(this.moveItem))
    461. // console.log(item)
    462. let _oldMouseX = e.clientX
    463. let _oldMouseY = e.clientY
    464. let _d = _oldMouseX - this.oldMouseX
    465. let _dy = _oldMouseY - this.oldMouseY
    466. this.oldMouseX = _oldMouseX
    467. this.oldMouseY = _oldMouseY
    468. // 最左侧/最右侧/最上侧/最底侧
    469. // if (item.tLeft + _d < this.minX || item.tLeft+item.tWidth + _d > this.maxX || item.tTop + _dy < this.minY || item.tTop + _dy + item.tHeight > this.maxY) {
    470. if (item.tLeft + _d < this.minX || item.tTop + _dy < this.minY || item.tTop + _dy + item.tHeight > this.maxY) {
    471. return
    472. }
    473. item.tLeft += _d
    474. item.tTop += _dy
    475. // 判断是否对齐
    476. let _e = this.showAlignLine(item)
    477. if(_e) {
    478. item.tLeft = _e.left
    479. }
    480. this.moveItem = JSON.parse(JSON.stringify(item))
    481. } else {
    482. for (let i = 0; i < this.items.length; i++) {
    483. if (this.moveItem.id == this.items[i].id) {
    484. let item = JSON.parse(JSON.stringify(this.items[i]))
    485. // 左拖中
    486. if (this.dragLeftAbled) {
    487. let _oldMouseX = e.clientX
    488. let _oldMouseY = e.clientY
    489. let _d = _oldMouseX - this.oldMouseX
    490. this.oldMouseX = _oldMouseX
    491. this.oldMouseY = _oldMouseY
    492. // 滑块最小宽度/最左侧
    493. if (item.tWidth - _d <= this.btnWidth || item.tLeft + _d < this.minX) {
    494. return
    495. }
    496. item.tWidth -= _d
    497. item.tLeft += _d
    498. // 判断是否对齐
    499. let _e = this.showAlignLine(item)
    500. if(_e) {
    501. this.oldMouseX += _e.n
    502. this.items[i].tWidth = item.tWidth + _e.n
    503. this.items[i].tLeft = _e.left
    504. } else {
    505. this.items[i] = JSON.parse(JSON.stringify(item))
    506. }
    507. }
    508. // 右拖中
    509. else if (this.dragRightAbled) {
    510. let _oldMouseX = e.clientX
    511. let _oldMouseY = e.clientY
    512. let _d = _oldMouseX - this.oldMouseX
    513. this.oldMouseX = _oldMouseX
    514. this.oldMouseY = _oldMouseY
    515. // 滑块最小宽度/最右侧
    516. // if (item.tWidth + _d <= this.btnWidth || item.tLeft + item.tWidth + _d > this.maxX) {
    517. if (item.tWidth + _d <= this.btnWidth) {
    518. return
    519. }
    520. item.tWidth += _d
    521. // 判断是否对齐
    522. let _e = this.showAlignLine(item)
    523. if(_e) {
    524. this.oldMouseX += _e.n
    525. this.items[i].tWidth = item.tWidth + _e.n
    526. } else {
    527. this.items[i] = JSON.parse(JSON.stringify(item))
    528. }
    529. this.updateCanvasDom()
    530. }
    531. break
    532. }
    533. }
    534. }
    535. }
    536. else {
    537. // 判断是否点击到标尺
    538. let _mX = e.clientX - this.tWrapEle.left + this.tWrapScrollLeft
    539. let _bcX = this.minX + this.currentTime*10
    540. if(_mX >= _bcX - 2 && _mX <= _bcX + 2) {
    541. this.tCanvas.style.cursor = 'grab'
    542. return
    543. }
    544. for (let i = 0; i < this.items.length; i++) {
    545. let item = JSON.parse(JSON.stringify(this.items[i]))
    546. item.tLeft = item.tLeft - this.tWrapScrollLeft
    547. item.tTop = item.tTop - this.tWrapScrollTop
    548. // 判断鼠标坐标是否在滑块上
    549. if (this.isInPolygon({
    550. x: e.clientX - this.tWrapEle.left,
    551. y: e.clientY - this.tWrapEle.top
    552. }, [
    553. {
    554. x: item.tLeft,
    555. y: item.tTop
    556. },
    557. {
    558. x: item.tLeft + item.tWidth,
    559. y: item.tTop
    560. },
    561. {
    562. x: item.tLeft + item.tWidth,
    563. y: item.tTop + item.tHeight
    564. },
    565. {
    566. x: item.tLeft,
    567. y: item.tTop + item.tHeight
    568. }
    569. ])) {
    570. if (item.active) {
    571. // 判断是否在左按钮上
    572. if (this.isInPolygon({
    573. x: e.clientX - this.tWrapEle.left,
    574. y: e.clientY - this.tWrapEle.top
    575. }, [
    576. {
    577. x: item.tLeft,
    578. y: item.tTop
    579. },
    580. {
    581. x: item.tLeft + this.btnWidth,
    582. y: item.tTop
    583. },
    584. {
    585. x: item.tLeft + this.btnWidth,
    586. y: item.tTop + item.tHeight
    587. },
    588. {
    589. x: item.tLeft,
    590. y: item.tTop + item.tHeight
    591. }
    592. ])) {
    593. this.tCanvas.style.cursor = 'w-resize'
    594. }
    595. // 判断是否在右按钮上
    596. else if (this.isInPolygon({
    597. x: e.clientX - this.tWrapEle.left,
    598. y: e.clientY - this.tWrapEle.top
    599. }, [
    600. {
    601. x: item.tLeft + item.tWidth - this.btnWidth,
    602. y: item.tTop
    603. },
    604. {
    605. x: item.tLeft + item.tWidth,
    606. y: item.tTop
    607. },
    608. {
    609. x: item.tLeft + item.tWidth,
    610. y: item.tTop + item.tHeight
    611. },
    612. {
    613. x: item.tLeft + item.tWidth - this.btnWidth,
    614. y: item.tTop + item.tHeight
    615. }
    616. ])) {
    617. this.tCanvas.style.cursor = 'e-resize'
    618. } else {
    619. this.tCanvas.style.cursor = 'grab'
    620. }
    621. } else {
    622. this.tCanvas.style.cursor = 'grab'
    623. }
    624. break
    625. } else {
    626. this.tCanvas.style.cursor = 'auto'
    627. }
    628. }
    629. }
    630. },
    631. /**
    632. * 鼠标松开
    633. * @param {*} e
    634. */
    635. cMouseUp(e) {
    636. if (this.moveAbled && this.moveItem) {
    637. for (let i = 0; i < this.items.length; i++) {
    638. // 判断中点是否在行内
    639. // let _cx = this.moveItem.tLeft + this.moveItem.tWidth / 2 + this.minX
    640. let _cy = this.moveItem.tTop + this.moveItem.tHeight / 2
    641. if (_cy > this.items[i].tTop && _cy < this.items[i].tTop + this.items[i].tHeight) {
    642. // console.log('在'+i+'行内')
    643. if (this.items[i].id !== this.moveItem.id) {
    644. let _oZindex = this.moveItem.zIndex
    645. let _nZindex = this.items[i].zIndex
    646. this.items[this.moveItem.index].zIndex = _nZindex
    647. this.items[this.moveItem.index].tLeft = this.moveItem.tLeft
    648. this.items[i].zIndex = _oZindex
    649. } else {
    650. this.items[i].tLeft = this.moveItem.tLeft
    651. }
    652. break
    653. }
    654. }
    655. }
    656. this.bcMoveAbled = false
    657. this.moveAbled = false
    658. this.dragLeftAbled = false
    659. this.dragRightAbled = false
    660. this.oldMouseX = 0
    661. this.oldMouseY = 0
    662. this.moveItem = null
    663. this.alignLine = null
    664. this.alignStaff = false
    665. this.updateCanvasDom()
    666. },
    667. doDrawTimeLine() {
    668. cancelAnimationFrame(this.requestAnimationFrameId)
    669. this.drawTimeLine()
    670. this.requestAnimationFrameId = requestAnimationFrame(this.doDrawTimeLine)
    671. },
    672. /**
    673. * 绘制时间轴
    674. */
    675. drawTimeLine() {
    676. // this.ctx.reset()
    677. this.ctx.clearRect(0, 0, this.cWidth, this.cHeight)
    678. // 绘制行数
    679. this.drawLine()
    680. // 绘制最右侧线条
    681. this.ctx.beginPath()
    682. this.ctx.moveTo(this.maxX - this.tWrapScrollLeft, 0)
    683. this.ctx.lineTo(this.maxX- this.tWrapScrollLeft, this.maxY)
    684. this.ctx.stroke()
    685. // 滑块绘制
    686. for (let i = 0; i < this.items.length; i++) {
    687. let item = JSON.parse(JSON.stringify(this.items[i]))
    688. item.tLeft = item.tLeft - this.tWrapScrollLeft
    689. item.tTop = item.tTop - this.tWrapScrollTop
    690. this.drawHk(item)
    691. if (this.moveAbled && this.moveItem) {
    692. let _item = JSON.parse(JSON.stringify(this.moveItem))
    693. _item.tLeft = _item.tLeft - this.tWrapScrollLeft
    694. _item.tTop = _item.tTop - this.tWrapScrollTop
    695. this.ctx.save()
    696. this.ctx.globalAlpha = 0.3
    697. this.drawHk(_item)
    698. this.ctx.restore()
    699. }
    700. }
    701. if(this.alignLine) {
    702. // 绘制对齐虚线
    703. this.ctx.save()
    704. this.ctx.strokeStyle = 'white'
    705. this.ctx.setLineDash([5,5])
    706. this.ctx.lineWidth = 2
    707. this.ctx.beginPath()
    708. this.ctx.moveTo(this.alignLine.left - this.tWrapScrollLeft, this.alignLine.top)
    709. this.ctx.lineTo(this.alignLine.left - + this.tWrapScrollLeft, this.alignLine.top+this.alignLine.height)
    710. this.ctx.stroke()
    711. this.ctx.restore()
    712. }
    713. // 绘制标尺
    714. this.drawStaff()
    715. },
    716. /**
    717. * 标尺绘制
    718. */
    719. drawStaff() {
    720. this.ctx.save()
    721. if(this.alignStaff) {
    722. this.ctx.fillStyle = 'pink'
    723. } else {
    724. this.ctx.fillStyle = 'white'
    725. }
    726. this.ctx.fillRect(this.minX + this.currentTime * 10 - 1 - this.tWrapScrollLeft, 0, 2, this.cHeight)
    727. this.ctx.restore()
    728. },
    729. /**
    730. * 行数绘制
    731. */
    732. drawLine() {
    733. for (let i = 0; i < this.items.length; i++) {
    734. this.ctx.save()
    735. this.ctx.beginPath()
    736. this.ctx.fillStyle = 'yellow'
    737. this.ctx.fillRect(this.minX - this.tWrapScrollLeft, this.minY + 5 + this.lineHeight * i + 5 * i - this.tWrapScrollTop, this.cMaxWidth, this.lineHeight)
    738. this.ctx.fill()
    739. this.ctx.restore()
    740. }
    741. },
    742. /**
    743. * 滑块绘制
    744. */
    745. drawHk(item) {
    746. // 绘制滑块
    747. this.ctx.save()
    748. this.ctx.fillStyle = 'red'
    749. this.ctx.beginPath()
    750. this.ctx.roundRect(item.tLeft, item.tTop, item.tWidth, item.tHeight, 3)
    751. // this.ctx.fillRect(item.tLeft, item.tTop, item.tWidth, item.tHeight)
    752. this.ctx.fill()
    753. this.ctx.restore()
    754. if (item.active) {
    755. // 绘制编辑框
    756. this.ctx.save()
    757. // 左按钮
    758. this.ctx.beginPath()
    759. this.ctx.roundRect(item.tLeft, item.tTop, this.btnWidth, item.tHeight, [3, 0, 0, 3])
    760. this.ctx.fillStyle = 'gray'
    761. this.ctx.fill()
    762. let _w = 2
    763. let _h = 12
    764. this.ctx.fillStyle = 'white'
    765. this.ctx.fillRect(item.tLeft + (this.btnWidth - _w * 3) / 2, item.tTop + (item.tHeight - _h) / 2, _w, _h)
    766. this.ctx.fillRect(item.tLeft + (this.btnWidth - _w * 3) / 2 + _w * 2, item.tTop + (item.tHeight - _h) / 2, _w, _h)
    767. // 右按钮
    768. this.ctx.beginPath()
    769. this.ctx.roundRect(item.tLeft + item.tWidth - this.btnWidth, item.tTop, this.btnWidth, item.tHeight, [0, 3, 3, 0])
    770. this.ctx.fillStyle = 'gray'
    771. this.ctx.fill()
    772. this.ctx.fillStyle = 'white'
    773. this.ctx.fillRect(item.tLeft + item.tWidth - this.btnWidth + (this.btnWidth - _w * 3) / 2, item.tTop + (item.tHeight - _h) / 2, _w, _h)
    774. this.ctx.fillRect(item.tLeft + item.tWidth - this.btnWidth + (this.btnWidth - _w * 3) / 2 + _w * 2, item.tTop + (item.tHeight - _h) / 2, _w, _h)
    775. // 外边框
    776. this.ctx.beginPath()
    777. this.ctx.strokeStyle = "black"
    778. this.ctx.lineWidth = 1
    779. this.ctx.roundRect(item.tLeft+1, item.tTop+1, item.tWidth-2, item.tHeight-2, 3)
    780. this.ctx.stroke()
    781. // 文本
    782. this.ctx.fillStyle = 'white'
    783. this.ctx.font = "20px serif"
    784. this.ctx.textBaseline = 'middle'
    785. this.ctx.fillText('测试文本sssssswqwqwqwqwqwq', item.tLeft + this.btnWidth + 10, item.tTop + item.tHeight / 2, item.tWidth - this.btnWidth * 2 - 20)
    786. this.ctx.restore()
    787. } else {
    788. // 文本
    789. this.ctx.fillStyle = 'white'
    790. this.ctx.font = "20px serif"
    791. this.ctx.textBaseline = 'middle'
    792. this.ctx.fillText('测试文本sssssswqwqwqwqwqwq', item.tLeft + this.btnWidth + 10, item.tTop + item.tHeight / 2, item.tWidth - this.btnWidth * 2 - 20)
    793. }
    794. }
    795. }
    796. }
    797. script>
    798. <style lang="scss" scoped>
    799. .main-container {
    800. margin: 50px;
    801. position: relative;
    802. width: 700px;
    803. height: 300px;
    804. background-color: green;
    805. overflow: auto;
    806. #tl-canvas {
    807. z-index: 11;
    808. position: sticky;
    809. top: 0;
    810. left: 0;
    811. width: 700px;
    812. height: 300px;
    813. }
    814. .hidden-box {
    815. position: absolute;
    816. top: 0;
    817. left: 0;
    818. z-index: -1;
    819. opacity: 0;
    820. width: 1000px;
    821. height: 500px;
    822. }
    823. }
    824. style>

     

     

  • 相关阅读:
    板卡的分级调试经验
    vue-admin-template改变接口地址
    【OpenVINO】量化流程
    微信小程序——CSS3渐变
    如何使用环境变量运行bat脚本(开启数据库db)
    竞赛选题 深度学习验证码识别 - 机器视觉 python opencv
    核密度分析
    蓝桥杯模拟赛:最远滑行距离 ← dfs
    Harmony Codelab 样例—弹窗基本使用
    【LRUCache】Python缓存装饰器
  • 原文地址:https://blog.csdn.net/qq_31851435/article/details/134039029