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

33 篇文章 0 订阅
29 篇文章 2 订阅

一、简介

使用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>




 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现一个canvas弧形滑块,可以按照以下步骤进行: 1. 引入vue和canvas,在vue组件中创建一个canvas元素。 2. 通过canvas API绘制一个弧形,可以使用arc方法绘制圆弧。 3. 绘制滑块,可以使用fillRect方法绘制一个矩形。 4. 监听鼠标事件(mousedown、mousemove、mouseup),根据鼠标位置计算滑块的位置,并重新绘制canvas。 下面是一个简单的实现代码: ```html <template> <canvas ref="canvas"></canvas> </template> <script> export default { mounted() { const canvas = this.$refs.canvas const ctx = canvas.getContext('2d') const width = canvas.width const height = canvas.height const centerX = width / 2 const centerY = height / 2 const radius = 100 const startAngle = Math.PI * 1.5 const endAngle = Math.PI * 2.5 let position = 0 let dragging = false function draw() { // 绘制弧形 ctx.beginPath() ctx.arc(centerX, centerY, radius, startAngle, endAngle) ctx.lineWidth = 10 ctx.strokeStyle = '#ddd' ctx.stroke() // 绘制滑块 const sliderWidth = 20 const sliderHeight = 40 const x = centerX + Math.cos(position) * radius - sliderWidth / 2 const y = centerY + Math.sin(position) * radius - sliderHeight / 2 ctx.fillStyle = '#aaa' ctx.fillRect(x, y, sliderWidth, sliderHeight) } function getPosition(x, y) { const dx = x - centerX const dy = y - centerY return Math.atan2(dy, dx) } function handleMouseDown(e) { const x = e.clientX - canvas.offsetLeft const y = e.clientY - canvas.offsetTop const distance = Math.sqrt(Math.pow(centerX - x, 2) + Math.pow(centerY - y, 2)) if (distance > radius - 10 && distance < radius + 10) { dragging = true } } function handleMouseMove(e) { if (dragging) { const x = e.clientX - canvas.offsetLeft const y = e.clientY - canvas.offsetTop position = getPosition(x, y) draw() } } function handleMouseUp() { dragging = false } draw() canvas.addEventListener('mousedown', handleMouseDown) canvas.addEventListener('mousemove', handleMouseMove) canvas.addEventListener('mouseup', handleMouseUp) } } </script> ``` 以上代码创建了一个canvas元素,并在mounted钩子函数中绘制了一个弧形和一个滑块。同时,监听了鼠标事件,在鼠标拖动时计算滑块的位置并重新绘制canvas

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值