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

一、简介

使用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 = 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*10+this.minX)// 移动对齐标尺if(this.moveAbled) {if(Math.abs(item.tLeft - _bcX) <= _n) {this.alignStaff = truereturn {left: _bcX}} else if(Math.abs(item.tLeft+item.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 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 = 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 <= _bcX+2) {console.log('点击标尺', this.currentTime)this.tCanvas.style.cursor = 'grab'this.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 = 'grab'this.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 = 'auto'this.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.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 += _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 = '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.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 = 'white'this.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.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 = 2let _h = 12this.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 = 1this.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.mzph.cn/news/119192.shtml

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

相关文章

VR全景拍摄市场需求有多大?适用于哪些行业?

随着VR全景技术的成熟&#xff0c;越来越多的商家开始借助VR全景来宣传推广自己的店铺&#xff0c;特别是5G时代的到来&#xff0c;VR全景逐渐被应用在我们的日常生活中的各个方面&#xff0c;VR全景拍摄的市场需求也正在逐步加大。 通过VR全景技术将线下商家的实景“搬到线上”…

RTE2023大会来袭,声网宣布首创广播级4K超高清实时互动体验

10月24日&#xff0c;由声网和RTE开发者社区联合主办的RTE2023第九届实时互联网大会在北京举办&#xff0c;声网与众多RTE领域技术专家、产品精英、创业者、开发者一起&#xff0c;共同开启了以“智能高清”为主题的全新探讨。本届RTE大会将持续2天&#xff0c;开展1场主论坛及…

vm_flutter

附件地址 https://buuoj.cn/match/matches/195/challenges#vm_flutter 可以在buu下载到。 flutter我也不会&#xff0c;只是这个题目加密算法全部在java层&#xff0c;其实就是一个异或和相加。 反编译 package k;import java.util.Stack;/* loaded from: classes.dex */ pu…

Python基础入门例程12-NP12 格式化输出(二)

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 解答&#xff1a; 说明&#xff1a; 描述 牛牛、牛妹和牛可乐都是Nowcoder的用户&#xff0c;某天Nowcoder的管理员希望将他们的用户名以某种格式进行显示&#xff0c; 现在给定他们三个当中的某一个名字name…

淘宝API接口获取商品信息,订单管理,库存管理,数据分析

在淘宝开放平台中&#xff0c;每个API接口都有相应的文档说明和授权机制&#xff0c;以确保数据的安全性和可靠性。开发者可以根据自己的需求选择相应的API接口&#xff0c;并根据文档说明进行调用和使用。 淘宝开放平台API接口是一套REST方式的开放应用程序编程接口&…

web自动化测试——跨平台设备管理方案Selenium Grid

跨平台设备管理方案Selenium Grid 一、Selenium Grid简介二、使用场景场景一&#xff1a; 实现分布式执行测试&#xff0c;提高执行效率场景二&#xff1a; 解决浏览器兼容性问题新特性 三、Selenium Grid4原理分析四、环境安装五、运行方式&#xff1a;单机运行 - 独立模式1. …

uni-app:引用文件的方法

绝对定位 ①import common from "/utils/common.js" ②import common from "utils/common.js" <template><view></view> </template> <script>import common from "/utils/common.js"export default {data() {ret…

HarmonyOS 音频开发指导:使用 OpenSL ES 开发音频播放功能

OpenSL ES 全称为 Open Sound Library for Embedded Systems&#xff0c;是一个嵌入式、跨平台、免费的音频处理库。为嵌入式移动多媒体设备上的应用开发者提供标准化、高性能、低延迟的 API。HarmonyOS 的 Native API 基于Khronos Group开发的OpenSL ES 1.0.1 API 规范实现&am…

uniapp--点击上传图片到oss再保存数据给后端接口

项目采用uniapp与uview2.0组件库 --1.0的也可以参考一下&#xff0c;大差不差 一、项目要求与样式图 点击上传n张图片到oss&#xff0c;然后点击提交给后端 二、思路 1、打开上传按钮&#xff0c;弹出框内出现上传图片和提交按钮 2、点击上传图片区域&#xff0c;打开本地图…

一、【Photoshop如何根据不同类型图像抠图】

文章目录 前言图形结构1、规则图形2、不规则图形 图形颜色1、轮廓清晰2、颜色分明 前言 当我们有抠图需求的时候&#xff0c;不要一开始就想着我怎么去把它抠出来&#xff0c;首先应该分析图形的特点&#xff0c;然后再去选取合适的工具&#xff0c;这样才可以做到事半功倍&am…

OpenWRT软路由web界面如何远程访问?

文章目录 1.openWRT安装cpolar2.配置远程访问地址3.固定公网地址 简单几步实现在公网环境下远程访问openWRT web 管理界面&#xff0c;使用cpolar内网穿透创建安全隧道映射openWRT web 界面面板443端口&#xff0c;无需公网IP&#xff0c;无需设置路由器。 1.openWRT安装cpola…

JSX基础语法

文章目录 认识JSX语法JSX是什么为什么Rect选择了JSXJSX书写规范JSX注释编写 JSX的基本使用JSX的事件绑定this绑定问题参数传递问题 JSX的条件渲染常见的条件渲染方式 JSX的列表渲染JSX的原理和本质JSX的本质虚拟DOM的创建过程 案例练习 认识JSX语法 // 1. 定义根组件 const el…

RK3568-适配at24c04模块

将at24c04模块连接到开发板i2c2总线上 i2ctool查看i2c2总线上都有哪些设备 UU表示设备地址的从设备被驱动占用,卸载对应的驱动后,UU就会变成从设备地址。at24c04模块设备地址 0x50和0x51是at24c04模块i2c芯片的设备地址。这个从芯片手册上也可以得知。A0 A1 A2表示的是模块对…

基于元模型优化算法的主从博弈多虚拟电厂动态定价和能量管理(matlab代码)

目录 1 主要内容 主从博弈模型 基于元模型的均衡算法流程图 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序复现《基于元模型优化算法的主从博弈多虚拟电厂动态定价和能量管理》模型&#xff0c;建立运营商和多虚拟电厂的一主多从博弈模型&#xff0c;研究运营商动态…

如何租用香港写字楼,需要注意哪些事项

1. 确定您的所需 你需要多少空间 在一切开始之前&#xff0c;您需要确切地知道您的业务(即您、您现有的员工和预计的招聘、您的访客或客户以及您想要的设施如食品储藏室、接待处、服务器机房甚至健身房&#xff0c;婴儿护理室等)&#xff0c;以实用面积计算需要多少空间。空间…

线性代数1:线性方程和系统

Digital Collection (staedelmuseum.de) 图片来自施泰德博物馆 一、前言 通过这些文章&#xff0c;我希望巩固我对这些基本概念的理解&#xff0c;同时如果可能的话&#xff0c;通过我希望成为一种基于直觉的数学学习方法为其他人提供额外的清晰度。如果有任何错误或机会需要我…

出差学小白知识No5:|Ubuntu上关联GitLab账号并下载项目(ssh key配置)

1 注冊自己的gitlab账户 有手就行 2 ubuntu安装git &#xff0c;并查看版本 sudo apt-get install git git --version 3 vim ~/.ssh/config Host gitlab.example.com User your_username Port 22 IdentityFile ~/.ssh/id_rsa PreferredAuthentications publickey 替换gitl…

C++多态的认识与理解

多态的概念 通俗来说&#xff0c;多态就是多种形态。具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 比方说买高铁票时&#xff0c;如果你是学生的话&#xff0c;买票就有优惠。如果你是军人的话&#xff0c;就可以优先买票。普通人的话&…

一文了解和使用nginx(附带图文)

前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一文了解和使用nginx 一. nginx 简介1.1 什么是 nginx 和可以做什么事情1.2 正向代理1.3 反向代理1.4 负载均衡1.5 SSL 配置1.6 管理…

苹果官宣新品发布会 10月31日发布会与Mac有关

10 月 25 日消息&#xff0c;苹果宣布将于北京时间 10 月 31 日上午 8 点举行主题为“来势迅猛”的线上特别活动&#xff0c;届时或将有新品发布。 这场发布会与以往不同&#xff0c;将在北京时间 10 月 31 日上午 8 点举行。有很多猜测认为苹果届时会发布新款 Mac 电脑&#x…