293 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div :class="wrapper" class="canvas" @click="onClick" @mousemove.stop="onMousemove" @mouseup="onMouseUp" @mouseleave="isHide = true" v-if="isInit">
 | |
|     <canvas :id="id" :style="{height: '52px'}" v-if="canvasWidth" :width="canvasWidth" height="52">
 | |
|     </canvas>
 | |
|     <div class="time" v-show="!isHide" :style="{left: left + 'px'}">{{ time }}</div>
 | |
|     <div class="time-scale" :style="{left: x + 'px'}">
 | |
|       <span></span>
 | |
|     </div>
 | |
|     <img @mousedown="onDragDown" class="drag-img" :style="{left: x + 'px'}" src="https://cdn.cunwuyun.cn/slw2.0/images/drag.png" />
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
|   export default {
 | |
|     props: ['isLiveing', 'times'],
 | |
| 
 | |
|     data () {
 | |
|       return {
 | |
|         ctx: null,
 | |
|         canvasWidth: '',
 | |
|         canvasHeight: '',
 | |
|         scale: 4,
 | |
|         time: '',
 | |
|         left: 0,
 | |
|         x: 0,
 | |
|         ratioW: '',
 | |
|         isHide: true,
 | |
|         isInit: false,
 | |
|         isChoose: false,
 | |
|         wrapper: `canvas-${new Date().getTime()}`,
 | |
|         id: `timeline-${new Date().getTime()}`,
 | |
|         timer: null
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     watch: {
 | |
|       isLiveing () {
 | |
|         this.countdown()
 | |
|       },
 | |
| 
 | |
|       times: {
 | |
|         deep: true,
 | |
|         handler (v) {
 | |
|           if (v.length && this.ctx) {
 | |
|             this.init()
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     mounted () {
 | |
|       this.$nextTick(() => {
 | |
|         this.init()
 | |
|       })
 | |
|     },
 | |
| 
 | |
|     destroyed () {
 | |
|       clearInterval(this.timer)
 | |
|     },
 | |
| 
 | |
|     methods: {
 | |
|       onMousemove (e) {
 | |
|         const canvasInfo = document.querySelector(`#${this.id}`).getBoundingClientRect()
 | |
|         const seconds = 24 * 60 * 60
 | |
| 
 | |
|         if (e.clientY - canvasInfo.top < 29) {
 | |
|           const x = e.clientX - canvasInfo.left
 | |
|           const unit = seconds / this.canvasWidth * x
 | |
|           this.left = x
 | |
|           this.time = this.secTotime(unit)
 | |
|           this.isHide = false
 | |
| 
 | |
|           if (!this.isChoose) return
 | |
|           this.x = e.clientX - canvasInfo.left
 | |
|           this.ratioW = this.x / this.canvasWidth
 | |
|         } else {
 | |
|           this.isHide = true
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       onClick (e) {
 | |
|         const canvasInfo = document.querySelector(`#${this.id}`).getBoundingClientRect()
 | |
| 
 | |
|         if (e.clientY - canvasInfo.top < 29) {
 | |
|           this.x = e.clientX - canvasInfo.left
 | |
|           clearInterval(this.timer)
 | |
|           this.timer = null
 | |
|           const time = this.secTotime((24 * 60 * 60) / this.canvasWidth * this.x)
 | |
| 
 | |
|           this.$emit('replay', time)
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       onDragDown () {
 | |
|         this.isChoose = true
 | |
|       },
 | |
| 
 | |
|       onMouseUp () {
 | |
|         if (!this.isChoose) return
 | |
| 
 | |
|         clearInterval(this.timer)
 | |
|         this.timer = null
 | |
|         this.isChoose = false
 | |
|         const time = this.secTotime((24 * 60 * 60) / this.canvasWidth * this.x)
 | |
|         this.$emit('replay', time)
 | |
|       },
 | |
| 
 | |
|       secTotime (s) {
 | |
|         let second = parseInt(s)
 | |
|         let minute = 0
 | |
|         let hour = 0
 | |
|         if (second > 60) {
 | |
|           minute = parseInt(second / 60)
 | |
|           second = parseInt(second % 60)
 | |
|           if (minute > 60) {
 | |
|             hour = parseInt(minute / 60)
 | |
|             minute = parseInt(minute % 60)
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         hour = `${parseInt(hour) > 9 ? parseInt(hour) : '0' + parseInt(hour)}`
 | |
|         minute = `${parseInt(minute) > 9 ? parseInt(minute) : '0' + parseInt(minute)}`
 | |
|         second = `${parseInt(second) > 9 ? parseInt(second) : '0' + parseInt(second)}`
 | |
|         return `${hour}:${minute}:${second}`
 | |
|       },
 | |
| 
 | |
|       init () {
 | |
|         if (this.timer) {
 | |
|           clearInterval(this.timer)
 | |
|           this.timer = null
 | |
|         }
 | |
|           
 | |
|         this.ratioW = this.x / this.canvasWidth
 | |
|         this.$nextTick(() => {
 | |
|           this.isInit = true
 | |
|           this.$nextTick(() => {
 | |
|             this.canvasWidth = document.querySelector(`.${this.wrapper}`).offsetWidth
 | |
|             this.canvasHeight = document.querySelector(`.${this.wrapper}`).offsetHeight
 | |
| 
 | |
|             this.$nextTick(() => {
 | |
|               const el = document.querySelector(`#${this.id}`)
 | |
|               this.ctx = el.getContext('2d')
 | |
|               this.ctx.width  = document.querySelector('.canvas').offsetWidth
 | |
|               this.ctx.height  = document.querySelector('.canvas').offsetHeight
 | |
| 
 | |
|               if (this.x > 0) {
 | |
|                 this.x = this.ratioW * this.canvasWidth
 | |
|               } else {
 | |
|                 this.initNowTime()
 | |
|               }
 | |
| 
 | |
|               this.renderTimeLine()
 | |
|               this.renderPlayback()
 | |
| 
 | |
|               this.countdown()
 | |
|             })
 | |
|           })
 | |
|         })
 | |
|       },
 | |
| 
 | |
|       countdown () {
 | |
|         this.timer = setInterval(() => {
 | |
|           if (this.isLiveing) {
 | |
|             this.initNowTime()
 | |
|           } else {
 | |
|             this.x = this.x + this.canvasWidth / (24 * 60 * 60)
 | |
|           }
 | |
|         }, 1000)
 | |
|       },
 | |
| 
 | |
|       initNowTime () {
 | |
|         const date = new Date()
 | |
|         const seconds = date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds()
 | |
| 
 | |
|         this.x = this.canvasWidth / (24 * 60 * 60) * seconds
 | |
|       },
 | |
| 
 | |
|       drawLine(ctx, options) {
 | |
|         const { beginX, beginY, endX, endY, lineColor, lineWidth } = options
 | |
|         ctx.lineWidth = lineWidth
 | |
|         ctx.strokeStyle = lineColor
 | |
|         ctx.beginPath()
 | |
|         ctx.moveTo(beginX, beginY)
 | |
|         ctx.lineTo(endX, endY)
 | |
|         ctx.closePath()
 | |
|         ctx.stroke()
 | |
|       },
 | |
| 
 | |
|       renderPlayback () {
 | |
|         const ctx = this.ctx
 | |
|         const unit = this.canvasWidth / (24 * 60 * 60)
 | |
|         this.times.forEach(item => {
 | |
|           this.drawLine(ctx, {
 | |
|             beginX: item.startTime * unit,
 | |
|             beginY: 28,
 | |
|             endX: item.startTime * unit,
 | |
|             endY: 0,
 | |
|             lineColor: 'rgba(0, 156, 255, 1)',
 | |
|             lineWidth: item.endTime * unit - item.startTime * unit
 | |
|           })
 | |
|         })
 | |
|       },
 | |
| 
 | |
|       renderTimeLine () {
 | |
|         const ctx = this.ctx
 | |
|         ctx.fillStyle = 'rgba(51, 60, 83, 0.8)'
 | |
|         ctx.fillRect(0, 0, this.canvasWidth, 28)
 | |
|         ctx.fillStyle = 'rgba(32, 40, 61, 0.8)'
 | |
|         ctx.fillRect(0, 28, this.canvasWidth, 24)
 | |
| 
 | |
|         
 | |
|         ctx.fillStyle = '#fff'
 | |
|         ctx.font = '12px Arial'
 | |
|         const w = this.canvasWidth / 24 
 | |
| 
 | |
|         for (let i = 1; i < 25; i ++) {
 | |
|           this.drawLine(ctx, {
 | |
|             beginX: i * w,
 | |
|             beginY: 28,
 | |
|             endX: i * w,
 | |
|             endY: i % this.scale === 0 ? 16 : 20,
 | |
|             lineColor: i % this.scale === 0 ? 'red' : '#000',
 | |
|             lineWidth: i % this.scale === 0 ? 1 : 1
 | |
|           })
 | |
| 
 | |
|           if (i % this.scale === 0) {
 | |
|             const text = (i < 10 ? '0' + i : i) + ': 00'
 | |
|             const textWidth = ctx.measureText(text).width
 | |
|             if (i === 24) {
 | |
|               ctx.fillText(text, i * w - textWidth, 44)
 | |
|             } else {
 | |
|               ctx.fillText(text, i * w - textWidth / 2, 44)
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (!this.times.length) {
 | |
|         ctx.stroke()
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| </script>
 | |
| 
 | |
| <style lang="scss" scoped>
 | |
|   .canvas {
 | |
|     position: relative;
 | |
|     width: 100%;
 | |
|     height: 52px;
 | |
| 
 | |
|     .drag-img {
 | |
|       position: absolute;
 | |
|       left: 0;
 | |
|       top: 0;
 | |
|       z-index: 1;
 | |
|       user-select: none;
 | |
|       cursor: e-resize;
 | |
|       -webkit-user-drag: none;
 | |
|       // transform: translateX(-50%);
 | |
|     }
 | |
| 
 | |
|     .time-scale {
 | |
|       display: flex;
 | |
|       position: absolute;
 | |
|       align-items: center;
 | |
|       justify-content: center;
 | |
|       left: 0;
 | |
|       bottom: 0;
 | |
|       z-index: 1;
 | |
|       user-select: none;
 | |
|       width: 12px;
 | |
|       height: 24px;
 | |
| 
 | |
|       span {
 | |
|         width: 2px;
 | |
|         height: 24px;
 | |
|         background: rgba(255, 255, 255, 0.8);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .time {
 | |
|       position: absolute;
 | |
|       bottom: 22px;
 | |
|       left: 0;
 | |
|       z-index: 1;
 | |
|       padding: 2px 4px;
 | |
|       font-size: 12px;
 | |
|       color: #fff;
 | |
|       background: rgba(0, 0, 0, 1);
 | |
|       transform: translate(-50%, 100%);
 | |
|     }
 | |
|   }
 | |
| </style> |