使用canvas实现时间轴上滑块的各种常用操作(仅供参考)
一、简介
使用canvas,模拟绘制时间轴区域,有时间刻度标尺,时间轴区域上会有多行,每行都有一个滑块。
1、时间刻度标尺可以拖动,会自动对齐整数点秒数,最小步数为0.1秒。
2、滑块可以自由拖动,当滑块处于选中状态时,左右两边会出现可拖动的按钮,用于拉伸宽度。
3、滑块之间可以自由拖动交换位置。
4、滑块与滑块之间对齐时会出现对齐虚线,滑块与刻度标尺对齐时,刻度标尺会变色用于提醒用户此时已对齐。
5、当滑块拉伸到最右侧区域时,右侧空间不足时,会自动增加右侧空间区域。而当做右侧滑块的位置往左移动时,如果出现右侧空间区域过大,则会自动减少右侧空间区域,始终保持右侧空间留白区域是预设的宽度。

二、案例代码
<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>
相关文章:
使用canvas实现时间轴上滑块的各种常用操作(仅供参考)
一、简介 使用canvas,模拟绘制时间轴区域,有时间刻度标尺,时间轴区域上会有多行,每行都有一个滑块。 1、时间刻度标尺可以拖动,会自动对齐整数点秒数,最小步数为0.1秒。 2、滑块可以自由拖动,…...
Netty优化-扩展自定义协议中的序列化算法
Netty优化-扩展自定义协议中的序列化算法 一. 优化与源码1. 优化1.1 扩展自定义协议中的序列化算法 一. 优化与源码 1. 优化 1.1 扩展自定义协议中的序列化算法 序列化,反序列化主要用在消息正文的转换上 序列化时,需要将 Java 对象变为要传输的数据…...
【Java网络编程】二
本文主要介绍了传输层的UDP协议和TCP协议,以及在Java中如何通过Socket套接字实现网络编程(内附UDP和TCP版本的回显服务器代码) 一.网络通信 网络编程,就是写一个应用程序,让这个程序可以使用网络通信,这里就…...
通过IP地址可以做什么
通过IP地址可以做很多事情,因为它是互联网通信的基础之一。本文将探讨IP地址的定义、用途以及一些可能的应用。 IP地址的用途 1. 设备标识:IP地址用于标识互联网上的每个设备,这包括计算机、服务器、路由器、智能手机等。它类似于我们日常生…...
前端 CSS 经典:clip、clip-path
1. clip 1.1 clip: auto | inherit | rect auto:默认,不裁剪 inherit:继承父级 clip 属性 rect:规则四边形裁剪 1.2 clip: rect(top, right, bottom, left) 注意: 1.裁剪只对 fixed 和 absolute 的元素有效。 2.top&…...
android 如何判断已配对的蓝牙是否打开了互联网访问开关
最近遇到一个需求,要判断已配对的蓝牙是否打开了互联网访问的开关。 经查看源码,得出以下方法。 1. 首先要判断蓝牙是否打开 2. 已打开的蓝牙是否已配对 3. 验证是否真正打开 /*** 是否打开蓝牙互联网访问*/SuppressLint("MissingPermission&quo…...
在Linux上实现ECAT主站
在Linux上实现ECAT主站 引言介绍EtherCATSOEM 使用下载ECAT主站编译 引言 EtherCAT由一个主站设备和多个从站设备组成。主站设备使用标准的以太网控制器,具有良好的兼容性,任何具有网络接口卡的计算机和具有以太网控制的嵌入式设备都可以作为EtherCAT的…...
Spring Cloud之服务熔断与降级(Hystrix)
目录 Hystrix 概念 作用 服务降级 简介 使用场景 接口降级 服务端服务降级 1.添加依赖 2.定义接口 3.实现接口 4.Controller类使用 5.启动类添加注释 6.浏览器访问 客户端服务降级 1.添加依赖 2.application.yml 中添加配置 3.定义接口 4.Controller类使用 …...
HashMap 哈希碰撞、负载因子、插入方式、扩容倍数
HashMap 怎么解决的哈希碰撞问题? 主要采用了链地址法。具体来说: 每个哈希桶不仅存储一个键-值对,而是存储一个链表或树结构。这样,具有相同哈希值的键-值对可以被存储在同一个哈希桶中,并通过链表或树结构来解决碰…...
【Unity3D】Unity与Android交互
1 Unity 发布 apk 1.1 安装 Android Build Support 在 Unity Hub 中打开添加模块窗口,操作如下。 选择 Android Build Support 安装,如下(笔者这里已安装过)。 创建一个 Unity 项目,依次点击【File→Build Settings→…...
信号去噪算法
引言 在实际世界中,我们所获得的信号通常都包含了各种干扰和噪音。这些噪音可能来自电子设备、环境条件或传感器本身,它们会损害信号的质量,降低信息提取的准确性。因此,信号去噪和降噪技术在科学、工程和医学领域中扮演着至关重…...
GPT带我学-设计模式-10观察者模式
1 请你介绍一下观察者模式 观察者模式(Observer Pattern)是一种设计模式,它定义了对象之间的一对多依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者&…...
JDK - 常用的设计模式
单例模式 : Runtime 类:Java 运行时环境是单例的,可以通过 Runtime.getRuntime() 方法获得实例。Calendar 类:Calendar.getInstance() 方法返回的是一个单例的 Calendar 实例。数据源连接池:连接池的管理通常采用单例模…...
华为OD机考算法题:寻找最大价值的矿堆
题目部分 题目寻找最大价值的矿堆难度难题目说明给你一个由 0(空地)、1(银矿)、2(金矿)组成的的地图,矿堆只能由上下左右相邻的金矿或银矿连接形成。超出地图范围可以认为是空地。 假设银矿价值…...
wf-docker集群搭建(未完结)
系列文章目录 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、redis集群二、mysql集群三、nacos集群1. 环境要求2. 拉取镜像2.1. 拉取镜像方式配置集群2.2. 自定义nacos镜像配置集群 3 自定义…...
uni-app 在 APP 端的版本强制更新与热更新
整包更新与热更新的区别 ① 整包更新是指下载完整 apk 文件进行覆盖安装 ② 热更新是指把 app 有改动的地方打包进 wgt 文件,只更新 wgt 文件中的内容,不进行整包安装,在用户视角也叫做省流量更新 版本号规则约束 建议严格遵循 Semantic …...
实在智能受邀参加第14届珠中江数字化应用大会,AI赋能智能制造,共话“湾区经验”
制造业是实体经济的主体,是技术创新的主战场,是供给侧结构性改革的重要领域。抢占新一轮产业竞争制高点,制造业的数字化转型已成为行业升级的必由之路。 10月21日,第14届“珠中江”(珠海、中山、江门)数字…...
Qt 窗口的尺寸
默认尺寸 对于一个Qt的窗口(继承于QWidget),获取其窗体尺寸的方法size(); 以一个Qt创建Qt Widgets Application项目的默认生成代码为基础,做如下测试 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent…...
游戏数据分析对于运营游戏平台的重要性
游戏数据分析对于运营游戏平台具有至关重要的意义,它可以提供深入的见解,帮助了解玩家行为、偏好和互动,从而优化游戏体验,提高玩家参与度和留存率。 首先,通过游戏数据分析,运营者可以了解玩家在游戏中的表…...
微信群发消息的正确打开方式,让你的社交更高效!
在当今的社交媒体时代,微信已经成为了我们生活中必不可少的一部分。而微信的群发消息功能,让我们可以方便地将信息一次性发送给多个联系人。然而,微信的群发消息功能有一个限制,即每次只能群发200个联系人。这对于需要发送消息给大…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
