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

news2024/9/22 1:41:38

一、简介

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

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

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

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

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

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

07c925f5a8664149b569f64b75e419cd.png

 

二、案例代码

<template>
	<div class="main-container" ref="tWrap" @scroll="tWrapScroll($event)">
		<canvas id="tl-canvas" ref="tl-canvas" width="700" height="300" @mousedown.stop.prevent="cMouseDown($event)"
			@mousemove.stop.prevent="cMouseMove($event)" @mouseup.stop.prevent="cMouseUp($event)"
			@mouseleave.stop.prevent="cMouseUp($event)"></canvas>
		<div class="hidden-box" :style="{
			width: cMaxWidth + 'px',
			height: cMaxHeight + 'px'
		}"></div>
	</div>
</template>

<script>

export default {
	data() {
		return {
			tWrapScrollTop: 0,
			tWrapScrollLeft: 0,
			tWrapEle: null,
			tCanvas: null,
			ctx: null,
			minY: 50,
			maxY: 500,
			minX: 10, // 可拖动的x轴最左侧
			maxX: 700, // 可拖动的x轴最右侧
			rDistant: 300, // 画布右侧留白区域距离
			cWidth: 700, // 画布的宽度
			cHeight: 300, // 画布的高度
			cMaxWidth: 1000, // 实际画布需要的宽度
			cMaxHeight: 500, // 实际画布需要的高度
			btnWidth: 20, // 左右按钮宽度
			lineHeight: 50, // 滑块高度

			moveItem: null, // 当前移动的滑块
			items: [
				{
					zIndex: 1,
					id: 1,
					active: false,
					tTop: 0,
					tLeft: 10,
					tWidth: 100,
					tHeight: 50
				},
				{
					zIndex: 2,
					id: 2,
					active: false,
					tTop: 0,
					tLeft: 10,
					tWidth: 150,
					tHeight: 50
				},
				{
					zIndex: 3,
					id: 3,
					active: false,
					tTop: 0,
					tLeft: 10,
					tWidth: 200,
					tHeight: 50
				},
			],

			bcMoveAbled: false, // 刻度尺可移动的标识
			moveAbled: false, // 滑块可移动的标识
			dragLeftAbled: false, // 滑块可左拖的标识
			dragRightAbled: false, // 滑块可右拖的标识
			oldMouseX: 0,
			oldMouseY: 0,

			alignLine: null, // 对齐虚线对象
			alignStaff: false, // 刻度尺对齐标识

			currentTime: 10, // 刻度尺当前对齐的时间
		}
	},

	mounted() {


		this.$nextTick(() => {
			this.tCanvas = document.getElementById('tl-canvas')
			this.ctx = this.tCanvas.getContext('2d')

			let twrap = this.$refs['tWrap'].getBoundingClientRect()
			this.tWrapEle = twrap

			this.updateCanvasDom()

			this.doDrawTimeLine()
		})
	},

	beforeUnmount() {
	
	},

	methods: {

		/**
		 * 监听滚动事件
		 * @param {*} e 
		 */
		tWrapScroll(e) {
			this.tWrapScrollTop = this.$refs['tWrap'].scrollTop
			this.tWrapScrollLeft = this.$refs['tWrap'].scrollLeft
			// console.log(this.$refs['tWrap'].scrollTop)
		},

		/**
		 * 判断点是否在多边形内
		 * @param {*} p 
		 * @param {*} ptPolygon 
		 */
		isInPolygon(p, ptPolygon) {
			let ncross = 0;
			for (let i = 0; i < ptPolygon.length; i++) {
				let p1 = ptPolygon[i];
				let p2 = ptPolygon[(i + 1) % ptPolygon.length]; // 相邻两条边p1,p2
				if (p1.y == p2.y) {
					continue;
				}
				if (p.y < Math.min(p1.y, p2.y)) {
					continue;
				}
				if (p.y >= Math.max(p1.y, p2.y)) {
					continue;
				}
				let x = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
				if (x > p.x) {
					ncross++; // 只统计单边交点
				}
			}

			return (ncross % 2 == 1);
		},

		/**
		 * 判断是否出现对齐虚线
		 */
		showAlignLine(item) {

			let _n = 3

			// 判断是否对齐刻度尺
			let _bcX = (this.currentTime*10+this.minX)

			// 移动对齐标尺
			if(this.moveAbled) {
				if(Math.abs(item.tLeft - _bcX) <= _n) {
					this.alignStaff = true
					return {
						left: _bcX
					}
				} else if(Math.abs(item.tLeft+item.tWidth - _bcX) <= _n) {
					this.alignStaff = true
					return {
						left: _bcX - item.tWidth
					}
				} else {
					this.alignStaff = false
				}
			} 
			// 左拖对齐标尺
			else if(this.dragLeftAbled) {
				
				if(Math.abs(item.tLeft - _bcX) <= _n) {
					this.alignStaff = true
					return {
						n: item.tLeft - _bcX,
						left: _bcX
					}
				} else {
					this.alignStaff = false
				}
			} 
			// 右拖对齐标尺
			else if(this.dragRightAbled) {
		
				if(Math.abs(item.tLeft + item.tWidth - _bcX) <= _n) {
					this.alignStaff = true
					return {
						n: _bcX - (item.tLeft + item.tWidth)
					}
				} else {
					this.alignStaff = false
				}
			}
			

			// 判断滑块之间的对齐
			for(let i=0; i < this.items.length; i++) {
				// 移动
				if(this.moveAbled && i !== this.moveItem.index) {
					if(Math.abs(item.tLeft - this.items[i].tLeft) <= _n) {
						this.alignLine = {
							top: 0,
							left: this.items[i].tLeft,
							height: this.cHeight
						}
						
						return {
							left: this.items[i].tLeft
						}
						break
					}
					else if(Math.abs(item.tLeft+item.tWidth - this.items[i].tLeft) <= _n ) {
						this.alignLine = {
							top: 0,
							left: this.items[i].tLeft,
							height: this.cHeight
						}

						return {
							left: this.items[i].tLeft - item.tWidth
						}
						break
					} 
					else if(Math.abs(this.items[i].tLeft+this.items[i].tWidth - item.tLeft) <= _n) {

						this.alignLine = {
							top: 0,
							left: this.items[i].tLeft+this.items[i].tWidth,
							height: this.cHeight
						}

						return {
							left: this.items[i].tLeft+this.items[i].tWidth
						}
						break

					} 
					else if(Math.abs(item.tLeft+item.tWidth - (this.items[i].tLeft+this.items[i].tWidth)) <= _n) {
						this.alignLine = {
							top: 0,
							left: this.items[i].tLeft+this.items[i].tWidth,
							height: this.cHeight
						}

						return {
							left: this.items[i].tLeft+this.items[i].tWidth - item.tWidth
						}
						break
					}					
				}
				// 左拖
				else if(this.dragLeftAbled && i !== this.moveItem.index) {
					if(Math.abs(item.tLeft - this.items[i].tLeft) <= _n) {
						this.alignLine = {
							top: 0,
							left: this.items[i].tLeft,
							height: this.cHeight
						}
						
						return {
							n: item.tLeft - this.items[i].tLeft,
							left: this.items[i].tLeft
						}
						break
					} 
					else if(Math.abs(this.items[i].tLeft+this.items[i].tWidth - item.tLeft) <= _n) {
						this.alignLine = {
							top: 0,
							left: this.items[i].tLeft+this.items[i].tWidth,
							height: this.cHeight
						}

						return {
							n: item.tLeft - (this.items[i].tLeft+this.items[i].tWidth),
							left: this.items[i].tLeft+this.items[i].tWidth
						}
						break
					}
				}
				// 右拖
				else if(this.dragRightAbled && i !== this.moveItem.index) {
					if(Math.abs(item.tLeft+item.tWidth - (this.items[i].tLeft+this.items[i].tWidth)) <= _n) {
						this.alignLine = {
							top: 0,
							left: this.items[i].tLeft+this.items[i].tWidth,
							height: this.cHeight
						}

						return {
							n: this.items[i].tLeft+this.items[i].tWidth - (item.tLeft+item.tWidth),
							// left: this.items[i].tLeft+this.items[i].tWidth - item.tWidth
						}
						break
					} 
					else if(Math.abs(item.tLeft+item.tWidth - this.items[i].tLeft) <= _n) {
						this.alignLine = {
							top: 0,
							left: this.items[i].tLeft,
							height: this.cHeight
						}

						return {
							n: this.items[i].tLeft - (item.tLeft+item.tWidth),
							// left: this.items[i].tLeft - item.tWidth
						}
						break
					}
				}
			}

			// 没有对齐
			this.alignLine = null
			return false
		},

		/**
		 * 检测当前滑块的最大长度和数量,随时更新画布的最大宽度和高度
		 */
		updateCanvasDom() {

			let maxWidth = 0

			// 按层级排序
			this.items.sort((a, b) => b.zIndex - a.zIndex)
	
			for (let i = 0; i < this.items.length; i++) {
				// 获取最大宽度
				maxWidth = this.items[i].tLeft + this.items[i].tWidth > maxWidth ? this.items[i].tLeft + this.items[i].tWidth : maxWidth

				// 重新更新y坐标
				this.items[i].tTop = 5 + this.lineHeight * i + 5 * i + this.minY
				
			}

			this.items = JSON.parse(JSON.stringify(this.items))
			

			// 留白区域大于预设
			if (this.cMaxWidth - maxWidth > this.rDistant && this.cMaxWidth - this.rDistant > this.cWidth) {
				this.cMaxWidth = maxWidth + this.rDistant
				this.maxX = this.cMaxWidth - this.rDistant
			}

			// 留白区域小于预设
			if (this.cMaxWidth - maxWidth < this.rDistant) {
				this.cMaxWidth += (this.rDistant - (this.cMaxWidth - maxWidth))
				this.maxX = this.cMaxWidth - this.rDistant
			}

			this.cMaxHeight = this.items.length * 55 > this.maxY ? this.items.length * 55 : this.maxY

		},

		/**
		 * 鼠标点击
		 */
		cMouseDown(e) {
			// 判断是否点击到标尺
			let _bcX = this.minX + this.currentTime*10
			let _mX = e.clientX - this.tWrapEle.left + this.tWrapScrollLeft
			if(_mX >= _bcX - 2 && _mX <= _bcX+2) {
				console.log('点击标尺', this.currentTime)
				this.tCanvas.style.cursor = 'grab'
				this.bcMoveAbled = true

				this.oldMouseX = e.clientX
				this.oldMouseY = e.clientY

				return
			}

			// 判断是否点击到滑块
			for (let i = 0; i < this.items.length; i++) {
				let item = JSON.parse(JSON.stringify(this.items[i]))

				item.tLeft = item.tLeft - this.tWrapScrollLeft
				item.tTop = item.tTop - this.tWrapScrollTop

				// 判断鼠标坐标是否在滑块上
				if (this.isInPolygon({
					x: e.clientX - this.tWrapEle.left,
					y: e.clientY - this.tWrapEle.top
				}, [
					{
						x: item.tLeft,
						y: item.tTop
					},
					{
						x: item.tLeft + item.tWidth,
						y: item.tTop
					},
					{
						x: item.tLeft + item.tWidth,
						y: item.tTop + item.tHeight
					},
					{
						x: item.tLeft,
						y: item.tTop + item.tHeight
					}
				])) {
					if (item.active) {

						// 判断是否在右按钮上
						if (this.isInPolygon({
							x: e.clientX - this.tWrapEle.left,
							y: e.clientY - this.tWrapEle.top
						}, [
							{
								x: item.tLeft + item.tWidth - this.btnWidth,
								y: item.tTop
							},
							{
								x: item.tLeft + item.tWidth,
								y: item.tTop
							},
							{
								x: item.tLeft + item.tWidth,
								y: item.tTop + item.tHeight
							},
							{
								x: item.tLeft + item.tWidth - this.btnWidth,
								y: item.tTop + item.tHeight
							}
						])) {

							this.dragRightAbled = true
							this.tCanvas.style.cursor = 'e-resize'

						}
						// 判断是否在左按钮上
						else if (this.isInPolygon({
							x: e.clientX - this.tWrapEle.left,
							y: e.clientY - this.tWrapEle.top
						}, [
							{
								x: item.tLeft,
								y: item.tTop
							},
							{
								x: item.tLeft + this.btnWidth,
								y: item.tTop
							},
							{
								x: item.tLeft + this.btnWidth,
								y: item.tTop + item.tHeight
							},
							{
								x: item.tLeft,
								y: item.tTop + item.tHeight
							}
						])) {

							this.dragLeftAbled = true
							this.tCanvas.style.cursor = 'w-resize'

						}
						// 在滑块上
						else {
							this.moveAbled = true
							this.tCanvas.style.cursor = 'grab'
						}
					} else {

						for (let i = 0; i < this.items.length; i++) {
							this.items[i].active = false
						}

						// 在滑块上
						this.tCanvas.style.cursor = 'grab'
						this.moveAbled = true
						this.items[i].active = true

					}

					// 保存移动的item
					this.moveItem = JSON.parse(JSON.stringify(this.items[i]))
					this.moveItem.index = i
					console.log('点击', this.moveItem)
					
					this.oldMouseX = e.clientX
					this.oldMouseY = e.clientY

					break

				} else {
					this.tCanvas.style.cursor = 'auto'
					this.items[i].active = false

					this.moveAbled = false
					this.dragLeftAbled = false
					this.dragRightAbled = false

					this.oldMouseX = 0
					this.oldMouseY = 0
				}
			}

		},

		/**
		 * 鼠标移动
		 */
		cMouseMove(e) {
	
			// 刻度尺
			if(this.bcMoveAbled) {
				let _oldMouseX = e.clientX
				
				let _d = _oldMouseX - this.oldMouseX

				this.oldMouseX = _oldMouseX

				let _n = 0.3

				let _time = this.currentTime + _d/10
				
				// 判断是否越界了
				if(_time < 0) {
					_time = 0
				}
				else if(_time * 10 + this.minX > this.maxX) {
					console.log('xxxx', this.maxX)
					_time = (this.maxX - this.minX)/10
				}
				// 判断是否移动到整数秒位置
				else if(Math.abs(Math.round(_time) - _time) <= _n) {
					this.oldMouseX += (Math.round(_time) - _time)*10
					_time = Math.round(_time)
					this.alignStaff = true
				} 
				else {
					this.alignStaff = false
				}


				this.currentTime = _time

				console.log(this.currentTime)
			}
			else if (this.moveItem) {
				// 移动中
				if (this.moveAbled) {
					let item = JSON.parse(JSON.stringify(this.moveItem))
					// console.log(item)

					let _oldMouseX = e.clientX
					let _oldMouseY = e.clientY

					let _d = _oldMouseX - this.oldMouseX
					let _dy = _oldMouseY - this.oldMouseY

					this.oldMouseX = _oldMouseX
					this.oldMouseY = _oldMouseY

					// 最左侧/最右侧/最上侧/最底侧
					// 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) {
					if (item.tLeft + _d < this.minX || item.tTop + _dy < this.minY || item.tTop + _dy + item.tHeight > this.maxY) {
						
						return
					}


					item.tLeft += _d
					item.tTop += _dy

					// 判断是否对齐
					let _e = this.showAlignLine(item)
					if(_e) {
						item.tLeft = _e.left
					}

					this.moveItem = JSON.parse(JSON.stringify(item))

				} else {
					for (let i = 0; i < this.items.length; i++) {

						if (this.moveItem.id == this.items[i].id) {

							let item = JSON.parse(JSON.stringify(this.items[i]))

							// 左拖中
							if (this.dragLeftAbled) {
								
								let _oldMouseX = e.clientX
								let _oldMouseY = e.clientY


								let _d = _oldMouseX - this.oldMouseX

								this.oldMouseX = _oldMouseX
								this.oldMouseY = _oldMouseY

								// 滑块最小宽度/最左侧
								if (item.tWidth - _d <= this.btnWidth || item.tLeft + _d < this.minX) {
									return
								}

								item.tWidth -= _d
								item.tLeft += _d

								// 判断是否对齐
								let _e = this.showAlignLine(item)
								if(_e) {
									this.oldMouseX += _e.n
									this.items[i].tWidth = item.tWidth + _e.n
									this.items[i].tLeft = _e.left
								} else {
									this.items[i] = JSON.parse(JSON.stringify(item))
								}

							}
							// 右拖中
							else if (this.dragRightAbled) {
								
								let _oldMouseX = e.clientX
								let _oldMouseY = e.clientY

								let _d = _oldMouseX - this.oldMouseX

								this.oldMouseX = _oldMouseX
								this.oldMouseY = _oldMouseY

								// 滑块最小宽度/最右侧
								// if (item.tWidth + _d <= this.btnWidth || item.tLeft + item.tWidth + _d > this.maxX) {
									if (item.tWidth + _d <= this.btnWidth) {
									return
								}

								item.tWidth += _d

								// 判断是否对齐
								let _e = this.showAlignLine(item)
								if(_e) {
									this.oldMouseX += _e.n
									this.items[i].tWidth = item.tWidth + _e.n
								} else {
									this.items[i] = JSON.parse(JSON.stringify(item))
								}

								this.updateCanvasDom()

							}

							break
						}

					}
				}

			} 
			else {

				// 判断是否点击到标尺
				let _mX = e.clientX - this.tWrapEle.left + this.tWrapScrollLeft
				let _bcX = this.minX + this.currentTime*10
				if(_mX >= _bcX - 2 && _mX <= _bcX + 2) {
					this.tCanvas.style.cursor = 'grab'
					return
				}

				for (let i = 0; i < this.items.length; i++) {

					let item = JSON.parse(JSON.stringify(this.items[i]))

					item.tLeft = item.tLeft - this.tWrapScrollLeft
					item.tTop = item.tTop - this.tWrapScrollTop

					// 判断鼠标坐标是否在滑块上
					if (this.isInPolygon({
						x: e.clientX - this.tWrapEle.left,
						y: e.clientY - this.tWrapEle.top
					}, [
						{
							x: item.tLeft,
							y: item.tTop
						},
						{
							x: item.tLeft + item.tWidth,
							y: item.tTop
						},
						{
							x: item.tLeft + item.tWidth,
							y: item.tTop + item.tHeight
						},
						{
							x: item.tLeft,
							y: item.tTop + item.tHeight
						}
					])) {
						if (item.active) {
							// 判断是否在左按钮上
							if (this.isInPolygon({
								x: e.clientX - this.tWrapEle.left,
								y: e.clientY - this.tWrapEle.top
							}, [
								{
									x: item.tLeft,
									y: item.tTop
								},
								{
									x: item.tLeft + this.btnWidth,
									y: item.tTop
								},
								{
									x: item.tLeft + this.btnWidth,
									y: item.tTop + item.tHeight
								},
								{
									x: item.tLeft,
									y: item.tTop + item.tHeight
								}
							])) {
								this.tCanvas.style.cursor = 'w-resize'
							}
							// 判断是否在右按钮上
							else if (this.isInPolygon({
								x: e.clientX - this.tWrapEle.left,
								y: e.clientY - this.tWrapEle.top
							}, [
								{
									x: item.tLeft + item.tWidth - this.btnWidth,
									y: item.tTop
								},
								{
									x: item.tLeft + item.tWidth,
									y: item.tTop
								},
								{
									x: item.tLeft + item.tWidth,
									y: item.tTop + item.tHeight
								},
								{
									x: item.tLeft + item.tWidth - this.btnWidth,
									y: item.tTop + item.tHeight
								}
							])) {
								this.tCanvas.style.cursor = 'e-resize'

							} else {
								this.tCanvas.style.cursor = 'grab'
							}
						} else {
							this.tCanvas.style.cursor = 'grab'
						}
						break
					} else {
						this.tCanvas.style.cursor = 'auto'
					}
				}
			}

		},

		/**
		 * 鼠标松开
		 * @param {*} e 
		 */
		cMouseUp(e) {

			if (this.moveAbled && this.moveItem) {

				for (let i = 0; i < this.items.length; i++) {
	
					// 判断中点是否在行内
					// let _cx = this.moveItem.tLeft + this.moveItem.tWidth / 2 + this.minX
					let _cy = this.moveItem.tTop + this.moveItem.tHeight / 2

					if (_cy > this.items[i].tTop && _cy < this.items[i].tTop + this.items[i].tHeight) {
						// console.log('在'+i+'行内')

						if (this.items[i].id !== this.moveItem.id) {
							let _oZindex = this.moveItem.zIndex
							let _nZindex = this.items[i].zIndex

							this.items[this.moveItem.index].zIndex = _nZindex
							this.items[this.moveItem.index].tLeft = this.moveItem.tLeft

							this.items[i].zIndex = _oZindex
						} else {
							this.items[i].tLeft = this.moveItem.tLeft
						}

						break
					}
				}

			}

			this.bcMoveAbled = false
			this.moveAbled = false
			this.dragLeftAbled = false
			this.dragRightAbled = false

			this.oldMouseX = 0
			this.oldMouseY = 0

			this.moveItem = null
			this.alignLine = null
			this.alignStaff = false

			this.updateCanvasDom()
		},

		doDrawTimeLine() {
			cancelAnimationFrame(this.requestAnimationFrameId)

			this.drawTimeLine()

			this.requestAnimationFrameId = requestAnimationFrame(this.doDrawTimeLine)
		},

		/**
		 * 绘制时间轴
		 */
		drawTimeLine() {

			// this.ctx.reset()
			this.ctx.clearRect(0, 0, this.cWidth, this.cHeight)

			// 绘制行数
			this.drawLine()

			// 绘制最右侧线条
			this.ctx.beginPath()
			this.ctx.moveTo(this.maxX - this.tWrapScrollLeft, 0)
			this.ctx.lineTo(this.maxX- this.tWrapScrollLeft, this.maxY)
			this.ctx.stroke()

			// 滑块绘制
			for (let i = 0; i < this.items.length; i++) {

				let item = JSON.parse(JSON.stringify(this.items[i]))

				item.tLeft = item.tLeft - this.tWrapScrollLeft
				item.tTop = item.tTop - this.tWrapScrollTop

				this.drawHk(item)

				if (this.moveAbled && this.moveItem) {

					let _item = JSON.parse(JSON.stringify(this.moveItem))

					_item.tLeft = _item.tLeft - this.tWrapScrollLeft
					_item.tTop = _item.tTop - this.tWrapScrollTop

					this.ctx.save()
					this.ctx.globalAlpha = 0.3
					this.drawHk(_item)
					this.ctx.restore()

				}

			}

			if(this.alignLine) {
				// 绘制对齐虚线
				this.ctx.save()
				this.ctx.strokeStyle = 'white'
				this.ctx.setLineDash([5,5])
				this.ctx.lineWidth = 2
				this.ctx.beginPath()
				this.ctx.moveTo(this.alignLine.left - this.tWrapScrollLeft, this.alignLine.top)
				this.ctx.lineTo(this.alignLine.left - + this.tWrapScrollLeft, this.alignLine.top+this.alignLine.height)
				this.ctx.stroke()
				this.ctx.restore()
			}

			// 绘制标尺
			this.drawStaff()

		},

		/**
		 * 标尺绘制
		 */
		drawStaff() {
			this.ctx.save()
			if(this.alignStaff) {
				this.ctx.fillStyle = 'pink'
			} else {
				this.ctx.fillStyle = 'white'
			}
			
			this.ctx.fillRect(this.minX + this.currentTime * 10 - 1 - this.tWrapScrollLeft, 0, 2, this.cHeight)
			this.ctx.restore()
		},

		/**
		 * 行数绘制
		 */
		drawLine() {

			for (let i = 0; i < this.items.length; i++) {
				this.ctx.save()
				this.ctx.beginPath()
				this.ctx.fillStyle = 'yellow'
				this.ctx.fillRect(this.minX - this.tWrapScrollLeft, this.minY + 5 + this.lineHeight * i + 5 * i - this.tWrapScrollTop, this.cMaxWidth, this.lineHeight)
				this.ctx.fill()
				this.ctx.restore()
			}

		},

		/**
		 * 滑块绘制
		 */
		drawHk(item) {
			// 绘制滑块
			this.ctx.save()
			this.ctx.fillStyle = 'red'

			this.ctx.beginPath()
			this.ctx.roundRect(item.tLeft, item.tTop, item.tWidth, item.tHeight, 3)
			// this.ctx.fillRect(item.tLeft, item.tTop, item.tWidth, item.tHeight)
			this.ctx.fill()
			this.ctx.restore()

			if (item.active) {
				// 绘制编辑框
				this.ctx.save()

				// 左按钮
				this.ctx.beginPath()
				this.ctx.roundRect(item.tLeft, item.tTop, this.btnWidth, item.tHeight, [3, 0, 0, 3])
				this.ctx.fillStyle = 'gray'
				this.ctx.fill()

				let _w = 2
				let _h = 12
				this.ctx.fillStyle = 'white'
				this.ctx.fillRect(item.tLeft + (this.btnWidth - _w * 3) / 2, item.tTop + (item.tHeight - _h) / 2, _w, _h)
				this.ctx.fillRect(item.tLeft + (this.btnWidth - _w * 3) / 2 + _w * 2, item.tTop + (item.tHeight - _h) / 2, _w, _h)


				// 右按钮
				this.ctx.beginPath()
				this.ctx.roundRect(item.tLeft + item.tWidth - this.btnWidth, item.tTop, this.btnWidth, item.tHeight, [0, 3, 3, 0])

				this.ctx.fillStyle = 'gray'
				this.ctx.fill()

				this.ctx.fillStyle = 'white'
				this.ctx.fillRect(item.tLeft + item.tWidth - this.btnWidth + (this.btnWidth - _w * 3) / 2, item.tTop + (item.tHeight - _h) / 2, _w, _h)
				this.ctx.fillRect(item.tLeft + item.tWidth - this.btnWidth + (this.btnWidth - _w * 3) / 2 + _w * 2, item.tTop + (item.tHeight - _h) / 2, _w, _h)

				// 外边框
				this.ctx.beginPath()
				this.ctx.strokeStyle = "black"
				this.ctx.lineWidth = 1
				this.ctx.roundRect(item.tLeft+1, item.tTop+1, item.tWidth-2, item.tHeight-2, 3)
				this.ctx.stroke()

				// 文本
				this.ctx.fillStyle = 'white'
				this.ctx.font = "20px serif"
				this.ctx.textBaseline = 'middle'
				this.ctx.fillText('测试文本sssssswqwqwqwqwqwq', item.tLeft + this.btnWidth + 10, item.tTop + item.tHeight / 2, item.tWidth - this.btnWidth * 2 - 20)

				this.ctx.restore()
			} else {
				// 文本
				this.ctx.fillStyle = 'white'
				this.ctx.font = "20px serif"
				this.ctx.textBaseline = 'middle'
				this.ctx.fillText('测试文本sssssswqwqwqwqwqwq', item.tLeft + this.btnWidth + 10, item.tTop + item.tHeight / 2, item.tWidth - this.btnWidth * 2 - 20)
			}
		}
	}
}

</script>

<style lang="scss" scoped>
.main-container {
	margin: 50px;
	position: relative;
	width: 700px;
	height: 300px;
	background-color: green;
	overflow: auto;

	#tl-canvas {
		z-index: 11;
		position: sticky;
		top: 0;
		left: 0;
		width: 700px;
		height: 300px;
	}

	.hidden-box {
		position: absolute;
		top: 0;
		left: 0;
		z-index: -1;
		opacity: 0;
		width: 1000px;
		height: 500px;
	}
}
</style>




 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1132336.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何将Linux上部署的5.7MySql数据库编码修改utf8(最新版)

如何将Linux&#xff08;服务器&#xff09;上部署的5.7MySql数据库编码修改utf8&#xff08;最新版&#xff09; 一、解决办法步骤1步骤2&#xff08;此处为问题描述吐槽&#xff0c;可以直接跳过该步骤到步骤三&#xff09;步骤3步骤4步骤5 二、结果 # 前言 提示&#xff1a…

【java学习—九】类的成员之四:初始化块(1)

文章目录 1. 初始化块(代码块)的作用2. 静态代码块3. 非静态代码块和静态代码块的特点 1. 初始化块(代码块)的作用 作用&#xff1a;对java对象进行初始化      程序执行的顺序&#xff1a;     ①声明成员变量的默认值 --> ②显式初始化、多个初始化块依次被执行&a…

On Moving Object Segmentation from Monocular Video with Transformers 论文阅读

论文信息 标题&#xff1a;On Moving Object Segmentation from Monocular Video with Transformers 作者&#xff1a; 来源&#xff1a;ICCV 时间&#xff1a;2023 代码地址&#xff1a;暂无 Abstract 通过单个移动摄像机进行移动对象检测和分割是一项具有挑战性的任务&am…

智慧停车视频解决方案:如何让AI助力停车管理升级?

一、项目背景 停车场的管理区域由于面积比较大&#xff0c;进出车辆多&#xff0c;所以在保安方面决不能有任何的麻痹和松懈&#xff0c;继续采用过去保安方式已远远不能满足现代安全防范的需求。为满足停车场的安全和科学系统化管理的需要&#xff0c;以及为了对随时发生的情…

希尔排序和直接插入排序代码对比

两段代码相似的地方也是两个算法一致的地方&#xff1a;即直接插入算法的整体流程和希尔算法的每个组内进行直接插入的流程。 不同点就是希尔算法会依次将整个序列分成…16、8、4、2、1组&#xff0c;每次在每个小组内进行直接插入&#xff0c;以此保证下一次扩大分组内元素数时…

用 Go 访问 MySql 数据库

所有代码样例 package mainimport ("database/sql""fmt"_ "github.com/go-sql-driver/mysql" )var db *sql.DB// 初始化连接 func initDB() (err error) {db, err sql.Open("mysql", "root:mm..1213tcp(127.0.0.1:3306)/chapte…

使用python实现http协议的方法

要使用Python编写一个接口&#xff0c;其入参格式为x-www-form-urlencoded&#xff0c;你可以使用requests库。requests库是一个流行的HTTP库&#xff0c;它使得发送HTTP请求变得非常简单。 下面是一个简单的示例&#xff0c;展示如何使用requests库发送一个POST请求&#xff…

苹果IOS系统企业IPA文件签名需要提供APP开源代码吗?

在科技发展日益迅速的今天&#xff0c;我们所生活的世界已变得越来越便捷。而在这个过程中&#xff0c;智能手机、移动应用以及与之相关的技术也在影响着我们日常生活的方方面面。苹果公司&#xff0c;作为行业巨头之一&#xff0c;不仅改变了全球智能手机的发展趋势&#xff0…

酒石酸盐晶体是优质葡萄酒的一个特征?

来自云仓酒庄品牌雷盛红酒分享澄清一下&#xff0c;葡萄酒中的酒石酸盐晶体&#xff0c;在德国被称为“温斯坦”&#xff0c;既无害也不是质量差的标志&#xff0c;相反&#xff0c;它们是富含矿物质的葡萄酒的特征。虽然酒石酸盐可以在年轻的葡萄酒中结晶&#xff0c;但它们最…

iconfont

iconfont-阿里巴巴矢量图标库https://www.iconfont.cn/ UI - 优设网 - 学设计上优设 (uisdc.com)https://www.uisdc.com/category/uiicon TreeNode moveNode (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode"); Point pt; …

C++问题总结

文章目录 C指针和引用的区别&#xff1a;悬空指针和野指针如何避免悬空指针和野指针多态析构函数可以为虚函数吗&#xff0c;为何建议为虚函数C程序的编译过程可以分为四个主要阶段线程池有死锁问题吗&#xff0c;死锁了解多少&#xff0c;递归锁知道吗常见的几种锁 网络什么是…

Pillow库(PIL)入门教程

Pillow库&#xff0c;PIL的升级版&#xff0c;一个免费开源的Python图像处理库。 Pillow 库&#xff08;有时也称 PIL 库&#xff09; 是 Python 图像处理的基础库&#xff0c;它是一个免费开源的第三方库&#xff0c;由一群 Python 社区志愿者使用 Python 语言开发而成&#…

LoongArch 指令集 流水线设计

简易版流水线 流水线总体思想-自己感悟 将指令执行分成若干个阶段&#xff08;五级流水-取值&#xff0c;译码&#xff0c;执行&#xff0c;访存&#xff0c;写回&#xff09;&#xff0c;每个阶段干自己的事&#xff08;生成相应的控制信号&#xff0c;完成自己的工作&#x…

PostgreSQL12中浮点数输出算法优化带来的小问题

最近碰到同事发来这样两个SQL&#xff0c;开发反馈输出的结果异常。 bill# select 0.1284*100::float;?column? --------------------12.839999999999998 (1 row)bill# select (0.1284*100)::float;float8 --------12.84 (1 row) 乍一看其实能看出明显的区别&#xff0c;由于…

CDN加速技术:国内外优劣势

在当今数字化时代&#xff0c;网站速度和性能对于用户体验和在线业务的成功至关重要。为了提供更快速的内容交付和优化用户体验&#xff0c;内容分发网络&#xff08;CDN&#xff09;技术应运而生。本文将分析CDN的国内外优劣势&#xff0c;探讨其价格因素&#xff0c;并通过实…

由浅入深,全面解析AMBA ACECHI协议

IC工程师&#xff0c;在设计芯片时&#xff0c;如果是基于各种复用IP的SOC芯片&#xff0c;那必定要接触到AMBA总线协议。 AMBA总线是由ARM公司提出的一种开放性的片上总线标准&#xff0c;它独立于处理器和工艺技术&#xff0c;具有高速度低功耗等特点。协议的主要动机是用一…

Android build.gradle读取String中文件及gradle.properties数据

在网上找了好久没有一个完整的学习文档&#xff0c;自己总结下&#xff0c;方便后面使用&#xff0c;话不多说直接上代码 首先获取路径 def path getProjectDir().getPath()"/src/"variant.productFlavors[0].name"/res/values/strings.xml"System.out…

音视频(二)之使用FFMpegSDK在我们自己的代码中进行推流

前言 上一篇文章我们介绍了如何使用FFMpeg工具进行推流。但如果要在我们的代码工程中&#xff0c;实现推流。就要下载FFMpegSDK包&#xff0c;调用相关API&#xff0c;在我们自己的程序中实现推流。 FFMpegSDK下载 下载动态库文件 选择这个下载 使用FFMpegSDK推流 下载FF…

jacob朗读中文,jacob生成中文语音音频文件,以生成MP3文件为例,不需要配置DLL

前言 本文使用jacob朗读文字和生成中文语音音频文件功能,与不同的是,本文不需要配置DLL到JVM根目录,只需要从项目类路径加载DLL即可。 jacob介绍 Jacob 是一个 Java 库,允许 Java 应用程序与Microsoft Windows DLL 或 COM 库进行通信。它通过使用自定义DLL来实现这一点,…