vue 实现canvas绘制地图,引用图片,对画布进行原点 滚轮缩放,按键缩放,拖动等功能 把pagex转化为offsetx
vue 实现canvas绘制地图,引用图片,对画布进行原点 滚轮缩放,按键缩放,拖动等功能
·
html
<div class="bigMap-box">
<el-dialog :visible.sync="bigMapDialog" :modal="false" :fullscreen="true" >
<div class="head-box">
<div class="head-text">
世界地图
</div>
<!-- 缩放按钮 -->
<div class="scalingmode">
<i class="el-icon-plus" id="plus" @click="plusClick(1)"></i>
<i class="el-icon-minus" id="minus" @click="plusClick(0)"></i>
</div>
<!-- 位置详情 -->
<!-- <div class="place-box" :style="{top:placeTop + 'px',left:placeLift + 'px'}"></div> -->
<img src="../assets/cha.png" class=" close" alt="" @click="closebigMap">
<img ref="bitmap" src="../assets/a1f0197aee81a9afaa3d0dacf2d8e96.jpg" style="display: none;">
<div class="canvas-box" ref="canvasBox">
<canvas ref="myCanvas" width="2264" height="1474" @mousewheel="onmousewheel" @click="findRectClick"
@mousedown="onmousedown" @mouseup="onmouseup" @mousemove="onmousemove"></canvas>
</div>
</div>
</el-dialog>
</div>
js
data () {
return {
bigMapDialog: false,
cvsCtx: null, // dom节点
canvas: null, //画布标签
img: null, //地图图片标签
stautsConfig: {
IDLE: 0, // 鼠标无操作状态
MOVE_START:1, //拖拽中
MOVING:4 //拖拽结束
},
canvasInfo : {
status: 0, //拖拽状态
offsetMouseEvtPos:{ x: null, y: null }, // 解决抖动问题 记录鼠标的位置
offset: { x: 0 ,y: 0}, // 画布偏移默认为0
scale: 1, // 缩放比例
scaleStep: 0.5, //滚轮放缩每滚一下增0.5或者将0.5
maxScale: 2, //最大放大倍数
minScale: 1, //最小缩小倍数
},
firstTime: '', // 按下时间
lastTime: '', // 抬起时间
stopClick: false //是否触发画布点击事件
}
},
created () {
// 监听键盘事件
window.addEventListener('keydown', e => {
if (e.key === 'm') {
this.openbigMap()
}
})
},
methods: {
/** 打开打地图事件 */
openbigMap () {
this.bigMapDialog = !this.bigMapDialog
if(this.bigMapDialog){
// 在dialog框生成后再去初始化canvas
this.$nextTick(() => {
this.getCanvas()
})
}
},
/** 关闭大地图事件 */
closebigMap () {
this.bigMapDialog = false
},
/** 绘制canvas */
getCanvas () {
this.canvas = this.$refs.myCanvas
this.cvsCtx = this.canvas.getContext('2d')
this.bitmap = this.$refs.bitmap
// 等待图片渲染完成后绘制
this.bitmap.onload = () => {
// 首次渲染默认显示中间区域 计算中间区域的偏移量
this.canvasInfo.offset.x = (this.$refs.canvasBox.offsetWidth -this.canvas.width * this.canvasInfo.scale) / 2
this.canvasInfo.offset.y = (this.$refs.canvasBox.offsetHeight -this.canvas.height * this.canvasInfo.scale) /2
this.drawImage() // 绘制图像
}
},
/** 图片转换canvas渲染 */
drawImage () {
// 重置前一个变换矩阵然后构建新的矩阵 a水平缩放绘图。b水平倾斜绘图。
// c垂直倾斜绘图 d 垂直缩放绘图。e 水平移动绘图。f 垂直移动绘图。
this.cvsCtx.setTransform(this.canvasInfo.scale, 0, 0,this.canvasInfo.scale, this.canvasInfo.offset.x, this.canvasInfo.offset.y )
// clearRect() 方法清空给定矩形内的指定像素。
this.cvsCtx.clearRect(0, 0, this.canvas.width * 2, this.canvas.height * 2)
this.cvsCtx.drawImage(this.bitmap,0, 0, this.canvas.width, this.canvas.width)
},
/** 画面位置转换为canvas位置坐标 */
getCanvasPosition(e,offset = {x: 0, y: 0},scale = 1){
// (鼠标点击的坐标-画布的偏移量) /缩放系数 = 画布的坐标
return {
x: (e.offsetX - offset.x) / scale,
y: (e.offsetY - offset.y)/ scale
}
},
/** 记录画面鼠标的位置 */
getMousePosition(e){
return {
x: e.offsetX,
y :e.offsetY
}
},
/** 鼠标滚轮事件 */
onmousewheel (e) {
e.preventDefault() //阻止默认滚轮事件
const canvasPosition = this.getCanvasPosition(e , this.canvasInfo.offset)
// 解构赋值
const { scaleStep } = this.canvasInfo
// 计算xy轴缩放时产生的偏移量 原点放大类似与盒子居中要减去自身的一半 原点缩小相反
const deltaX = canvasPosition.x / this.canvasInfo.scale * scaleStep
const deltaY = canvasPosition.y / this.canvasInfo.scale * scaleStep
if (e.deltaY > 0 && this.canvasInfo.scale < this.canvasInfo.maxScale) {
this.canvasInfo.offset.x -= deltaX
this.canvasInfo.offset.y -= deltaY
this.canvasInfo.scale += scaleStep
} else if (e.deltaY < 0 && this.canvasInfo.scale > this.canvasInfo.minScale) {
this.canvasInfo.offset.x += deltaX
this.canvasInfo.offset.y += deltaY
this.canvasInfo.scale -= scaleStep
}
this.preventNull()
this.drawImage ()
},
/** 鼠标按下 */
onmousedown (e) {
this.firstTime=new Date().getTime() // 记录按下时间
this.canvasInfo.status = this.stautsConfig.MOVE_START // 记录鼠标进入1状态
this.canvasInfo.offsetMouseEvtPos = this.getMousePosition(e) // 记录鼠标的坐标点
},
/** 鼠标弹起 */
onmouseup () {
this.canvas.style.cursor = 'default'
this.lastTime = new Date().getTime()
// 根据鼠标按下抬起的时间间隔判断是否触发鼠标点击事件小于200触发点击事件
if(this.lastTime - this.firstTime<200){
this.stopClick = true
}else {
this.stopClick = false
}
this.canvasInfo.status = this.stautsConfig.IDLE;
},
/** 鼠标移动 */
onmousemove (e) {
// const canvasPosition = this.getCanvasPosition(e , this.canvasInfo.offset, this.canvasInfo.scale)
// 判断鼠标状态是否按下
if(this.canvasInfo.status === this.stautsConfig.MOVE_START ) {
this.canvas.style.cursor = 'move'
this.canvasInfo.status = this.stautsConfig.MOVING //切换状态2 记录上一个偏移位置
this.canvasInfo.offsetMouseEvtPos = this.getMousePosition(e) // 记录鼠标的坐标点 记录上一个偏移位置
}else if (this.canvasInfo.status === this.stautsConfig.MOVING ) {
// 计算偏移量 += 新的鼠标位置 - 上一个鼠标位置。 鼠标移动了多少相当于画布偏移了多少
const mousePosition = this.getMousePosition(e)
this.canvasInfo.offset.x += mousePosition.x - this.canvasInfo.offsetMouseEvtPos.x
this.canvasInfo.offset.y += mousePosition.y - this.canvasInfo.offsetMouseEvtPos.y
// preventNull函数要放到drawImage渲染函数上面否则会出现画面抖动
this.preventNull()
this.drawImage ()
this.canvasInfo.offsetMouseEvtPos = mousePosition // 重置记录鼠标位置
}
},
/** 防止容器留白 判断四个边界*/
preventNull () {
// 计算四个边界边界 阻止留白 限制最小偏移量为0 最大偏移量为 外部盒子的宽度-(画布宽度*缩放系数)用于缩放拖动
var adaptSizeX = this.$refs.canvasBox.offsetWidth -(this.canvas.width * this.canvasInfo.scale)
var adaptSizeY = this.$refs.canvasBox.offsetHeight -(this.canvas.height * this.canvasInfo.scale)
if(this.canvasInfo.offset.x>0){
this.canvasInfo.offset.x = 0
}else if (this.canvasInfo.offset.x < adaptSizeX){
this.canvasInfo.offset.x = adaptSizeX
}
if(this.canvasInfo.offset.y>0){
this.canvasInfo.offset.y = 0
}else if(this.canvasInfo.offset.y < adaptSizeY){
this.canvasInfo.offset.y = adaptSizeY
}
},
/** 鼠标点击 */
findRectClick (e) {
if(this.stopClick){
console.log(222)
}
},
/** 缩放按钮点击事件 */
plusClick (num) {
console.log(num);
if (num == 1 && this.canvasInfo.scale < this.canvasInfo.maxScale) {
this.canvasInfo.scale += this.canvasInfo.scaleStep
} else if (num == 0 && this.canvasInfo.scale > this.canvasInfo.minScale) {
this.canvasInfo.scale -= this.canvasInfo.scaleStep
}
this.preventNull()
this.drawImage ()
},
}
css
<style lang="less" scope="scope">
.head-box{
position: relative;
.head-text{
position: absolute;
background-color: #fff;
top: 0;
left: 0;
width: 100%;
height: 60px;
text-align: center;
font-size: 30px;
font-weight: 500;
line-height: 58px;
color: #333333;
}
.close{
position: absolute;
top: 100px;
right: 140px;
width: 50px;
height: 50px;
cursor: pointer;
}
.scalingmode{
position: absolute;
top: 100px;
left: 140px;
width: 40px;
height: 130px;
border: 1px solid red;
text-align: center;
.el-icon-plus{
color: red;
font-size: 28px;
margin-top: 10px;
cursor: pointer;
}
.el-icon-minus{
color: red;
font-size: 28px;
margin-top: 50px;
cursor: pointer;
}
}
.place-box{
position: absolute;
width: 200px;
height: 100px;
background-color: aqua;
z-index: 1;
}
.canvas-box{
width: 98vw;
height: 98vh;
margin: 0 auto;
overflow: hidden;
}
}
.el-dialog__header{
display: none;
}
.el-dialog__body{
padding: 0;
overflow: hidden;
}
</style>
追加 由于手机触摸事件 没有offset属性 追加转换代码
// 把pagex转化为offsetx 触摸事件无offset属性
_touchStart (e) {
let imgTop = this.$refs.canvasBox.getBoundingClientRect().top
let imgLeft = this.$refs.canvasBox.getBoundingClientRect().left
let offsetX = Math.round(e.changedTouches[0].pageX - imgLeft)
let offsetY = Math.round(e.changedTouches[0].pageY - imgTop)
let el = {
offsetX,
offsetY
}
return el
}
更多推荐
已为社区贡献1条内容
所有评论(0)