pc端的网站设计方案,微信wordpress插件,wordpress app推广,网站有那些风格一、简介
使用canvas#xff0c;模拟绘制时间轴区域#xff0c;有时间刻度标尺#xff0c;时间轴区域上会有多行#xff0c;每行都有一个滑块。
1、时间刻度标尺可以拖动#xff0c;会自动对齐整数点秒数#xff0c;最小步数为0.1秒。
2、滑块可以自由拖动#xff0c…一、简介
使用canvas模拟绘制时间轴区域有时间刻度标尺时间轴区域上会有多行每行都有一个滑块。
1、时间刻度标尺可以拖动会自动对齐整数点秒数最小步数为0.1秒。
2、滑块可以自由拖动当滑块处于选中状态时左右两边会出现可拖动的按钮用于拉伸宽度。
3、滑块之间可以自由拖动交换位置。
4、滑块与滑块之间对齐时会出现对齐虚线滑块与刻度标尺对齐时刻度标尺会变色用于提醒用户此时已对齐。
5、当滑块拉伸到最右侧区域时右侧空间不足时会自动增加右侧空间区域。而当做右侧滑块的位置往左移动时如果出现右侧空间区域过大则会自动减少右侧空间区域始终保持右侧空间留白区域是预设的宽度。 二、案例代码
templatediv classmain-container reftWrap scrolltWrapScroll($event)canvas idtl-canvas reftl-canvas width700 height300 mousedown.stop.preventcMouseDown($event)mousemove.stop.preventcMouseMove($event) mouseup.stop.preventcMouseUp($event)mouseleave.stop.preventcMouseUp($event)/canvasdiv classhidden-box :style{width: cMaxWidth px,height: cMaxHeight px}/div/div
/templatescriptexport 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 twrapthis.updateCanvasDom()this.doDrawTimeLine()})},beforeUnmount() {},methods: {/*** 监听滚动事件* param {*} e */tWrapScroll(e) {this.tWrapScrollTop this.$refs[tWrap].scrollTopthis.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,p2if (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*10this.minX)// 移动对齐标尺if(this.moveAbled) {if(Math.abs(item.tLeft - _bcX) _n) {this.alignStaff truereturn {left: _bcX}} else if(Math.abs(item.tLeftitem.tWidth - _bcX) _n) {this.alignStaff truereturn {left: _bcX - item.tWidth}} else {this.alignStaff false}} // 左拖对齐标尺else if(this.dragLeftAbled) {if(Math.abs(item.tLeft - _bcX) _n) {this.alignStaff truereturn {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 truereturn {n: _bcX - (item.tLeft item.tWidth)}} else {this.alignStaff false}}// 判断滑块之间的对齐for(let i0; 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.tLeftitem.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].tLeftthis.items[i].tWidth - item.tLeft) _n) {this.alignLine {top: 0,left: this.items[i].tLeftthis.items[i].tWidth,height: this.cHeight}return {left: this.items[i].tLeftthis.items[i].tWidth}break} else if(Math.abs(item.tLeftitem.tWidth - (this.items[i].tLeftthis.items[i].tWidth)) _n) {this.alignLine {top: 0,left: this.items[i].tLeftthis.items[i].tWidth,height: this.cHeight}return {left: this.items[i].tLeftthis.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].tLeftthis.items[i].tWidth - item.tLeft) _n) {this.alignLine {top: 0,left: this.items[i].tLeftthis.items[i].tWidth,height: this.cHeight}return {n: item.tLeft - (this.items[i].tLeftthis.items[i].tWidth),left: this.items[i].tLeftthis.items[i].tWidth}break}}// 右拖else if(this.dragRightAbled i ! this.moveItem.index) {if(Math.abs(item.tLeftitem.tWidth - (this.items[i].tLeftthis.items[i].tWidth)) _n) {this.alignLine {top: 0,left: this.items[i].tLeftthis.items[i].tWidth,height: this.cHeight}return {n: this.items[i].tLeftthis.items[i].tWidth - (item.tLeftitem.tWidth),// left: this.items[i].tLeftthis.items[i].tWidth - item.tWidth}break} else if(Math.abs(item.tLeftitem.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.tLeftitem.tWidth),// left: this.items[i].tLeft - item.tWidth}break}}}// 没有对齐this.alignLine nullreturn 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.rDistantthis.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*10let _mX e.clientX - this.tWrapEle.left this.tWrapScrollLeftif(_mX _bcX - 2 _mX _bcX2) {console.log(点击标尺, this.currentTime)this.tCanvas.style.cursor grabthis.bcMoveAbled truethis.oldMouseX e.clientXthis.oldMouseY e.clientYreturn}// 判断是否点击到滑块for (let i 0; i this.items.length; i) {let item JSON.parse(JSON.stringify(this.items[i]))item.tLeft item.tLeft - this.tWrapScrollLeftitem.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 truethis.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 truethis.tCanvas.style.cursor w-resize}// 在滑块上else {this.moveAbled truethis.tCanvas.style.cursor grab}} else {for (let i 0; i this.items.length; i) {this.items[i].active false}// 在滑块上this.tCanvas.style.cursor grabthis.moveAbled truethis.items[i].active true}// 保存移动的itemthis.moveItem JSON.parse(JSON.stringify(this.items[i]))this.moveItem.index iconsole.log(点击, this.moveItem)this.oldMouseX e.clientXthis.oldMouseY e.clientYbreak} else {this.tCanvas.style.cursor autothis.items[i].active falsethis.moveAbled falsethis.dragLeftAbled falsethis.dragRightAbled falsethis.oldMouseX 0this.oldMouseY 0}}},/*** 鼠标移动*/cMouseMove(e) {// 刻度尺if(this.bcMoveAbled) {let _oldMouseX e.clientXlet _d _oldMouseX - this.oldMouseXthis.oldMouseX _oldMouseXlet _n 0.3let _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 _timeconsole.log(this.currentTime)}else if (this.moveItem) {// 移动中if (this.moveAbled) {let item JSON.parse(JSON.stringify(this.moveItem))// console.log(item)let _oldMouseX e.clientXlet _oldMouseY e.clientYlet _d _oldMouseX - this.oldMouseXlet _dy _oldMouseY - this.oldMouseYthis.oldMouseX _oldMouseXthis.oldMouseY _oldMouseY// 最左侧/最右侧/最上侧/最底侧// if (item.tLeft _d this.minX || item.tLeftitem.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 _ditem.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.clientXlet _oldMouseY e.clientYlet _d _oldMouseX - this.oldMouseXthis.oldMouseX _oldMouseXthis.oldMouseY _oldMouseY// 滑块最小宽度/最左侧if (item.tWidth - _d this.btnWidth || item.tLeft _d this.minX) {return}item.tWidth - _ditem.tLeft _d// 判断是否对齐let _e this.showAlignLine(item)if(_e) {this.oldMouseX _e.nthis.items[i].tWidth item.tWidth _e.nthis.items[i].tLeft _e.left} else {this.items[i] JSON.parse(JSON.stringify(item))}}// 右拖中else if (this.dragRightAbled) {let _oldMouseX e.clientXlet _oldMouseY e.clientYlet _d _oldMouseX - this.oldMouseXthis.oldMouseX _oldMouseXthis.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.nthis.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.tWrapScrollLeftlet _bcX this.minX this.currentTime*10if(_mX _bcX - 2 _mX _bcX 2) {this.tCanvas.style.cursor grabreturn}for (let i 0; i this.items.length; i) {let item JSON.parse(JSON.stringify(this.items[i]))item.tLeft item.tLeft - this.tWrapScrollLeftitem.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.minXlet _cy this.moveItem.tTop this.moveItem.tHeight / 2if (_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.zIndexlet _nZindex this.items[i].zIndexthis.items[this.moveItem.index].zIndex _nZindexthis.items[this.moveItem.index].tLeft this.moveItem.tLeftthis.items[i].zIndex _oZindex} else {this.items[i].tLeft this.moveItem.tLeft}break}}}this.bcMoveAbled falsethis.moveAbled falsethis.dragLeftAbled falsethis.dragRightAbled falsethis.oldMouseX 0this.oldMouseY 0this.moveItem nullthis.alignLine nullthis.alignStaff falsethis.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.tWrapScrollLeftitem.tTop item.tTop - this.tWrapScrollTopthis.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.tWrapScrollTopthis.ctx.save()this.ctx.globalAlpha 0.3this.drawHk(_item)this.ctx.restore()}}if(this.alignLine) {// 绘制对齐虚线this.ctx.save()this.ctx.strokeStyle whitethis.ctx.setLineDash([5,5])this.ctx.lineWidth 2this.ctx.beginPath()this.ctx.moveTo(this.alignLine.left - this.tWrapScrollLeft, this.alignLine.top)this.ctx.lineTo(this.alignLine.left - this.tWrapScrollLeft, this.alignLine.topthis.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 yellowthis.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 redthis.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 graythis.ctx.fill()let _w 2let _h 12this.ctx.fillStyle whitethis.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 graythis.ctx.fill()this.ctx.fillStyle whitethis.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 blackthis.ctx.lineWidth 1this.ctx.roundRect(item.tLeft1, item.tTop1, item.tWidth-2, item.tHeight-2, 3)this.ctx.stroke()// 文本this.ctx.fillStyle whitethis.ctx.font 20px serifthis.ctx.textBaseline middlethis.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 whitethis.ctx.font 20px serifthis.ctx.textBaseline middlethis.ctx.fillText(测试文本sssssswqwqwqwqwqwq, item.tLeft this.btnWidth 10, item.tTop item.tHeight / 2, item.tWidth - this.btnWidth * 2 - 20)}}}
}/scriptstyle langscss 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