0


小程序canvas 缩放/拖动/还原/封装和实例--开箱即用

小程序canvas 缩放/拖动/还原/封装和实例

一、预览

之前写过web端的canvas 缩放/拖动/还原/封装和实例。最近小程序也需要用到,但凡是涉及小程序canvas还是比较多坑的,而且难用多了,于是在web的基础上重新写了小程序的相关功能。实现功能有:

  • 支持双指、按钮缩放
  • 支持触摸拖动
  • 支持高清显示
  • 支持节流绘图
  • 支持还原、清除画布
  • 内置简化绘图方法

效果如下:
请添加图片描述

二、使用

案例涉及到2个文件,一个是绘图组件canvas.vue,另一个是canvasDraw.js,核心是canvasDraw.js里定义的CanvasDraw类

2.1 创建和配置

小程序获取#canvas对象后就可以创建CanvasDraw实例了,创建实例时可以根据需要设置各种配置,其中drawCallBack是必须的,是用户自定义的绘图方法,程序会在this.canvasDraw.draw()后再回调drawCallBack()来实现用户的绘图。
拖动、缩放画布都会调用this.canvasDraw.draw()。

/** 初始化canvas */initCanvas(){const query = wx.createSelectorQuery().in(this)

      query
        .select('#canvas').fields({node:true,size:true,rect:true}).exec((res)=>{const ele = res[0]this.canvasEle = ele

          // 配置项const option ={ele:this.canvasEle,// canvas元素drawCallBack:this.draw,// 必须:用户自定义绘图方法scale:1,// 当前缩放倍数scaleStep:0.1,// 缩放步长(按钮)touchScaleStep:0.005,// 缩放步长(手势)maxScale:2,// 缩放最大倍数(缩放比率倍数)minScale:0.5,// 缩放最小倍数(缩放比率倍数)translate:{x:0,y:0},// 默认画布偏移isThrottleDraw:true,// 是否开启节流绘图(建议开启,否则安卓调用频繁导致卡顿)throttleInterval:20,// 节流绘图间隔,单位mspixelRatio: wx.getSystemInfoSync().pixelRatio,// 像素比(高像素比可以解决高清屏幕模糊问题)}this.canvasDraw =newCanvasDraw(option)// 创建CanvasDraw实例后就可以使用实例的所有方法了this.canvasDraw.draw()// 可以按实际需要调用绘图方法})},

方法

canvasDraw.draw()// 绘图
canvasDraw.clear()// 清除画布
canvasDraw.reset()// 重置画布(恢复到第一次绘制的状态)
canvasDraw.zoomIn()// 中心放大
canvasDraw.zoomOut()// 中心缩小
canvasDraw.zoomTo(scale, zoomCenter)// 缩放到指定倍数(可指定缩放中心点)
canvasDraw.destory()// 销毁
canvasDraw.drawShape(opt)// 内置简化绘制多边形方法
canvasDraw.drawLines(opt)// 内置简化绘制多线段方法
canvasDraw.drawText(opt)// 内置简化绘制文字方法

三、源码

3.1 实例组件

canvas.vue

<template><viewclass="canvas-wrap"><canvastype="2d"id="canvas"class="canvas"disable-scroll="true"@touchstart="touchstart"@touchmove="touchmove"@touchend="touchend"@tap="tap"></canvas></view></template><script>import{ CanvasDraw }from'./canvasDraw'exportdefault{data(){this.canvasDraw =null// 绘图对象this.canvasEle =null// canvas元素对象return{}},created(){},beforeDestroy(){/** 销毁对象 */if(this.canvasDraw){this.canvasDraw.destroy()this.canvasDraw =null}},mounted(){/** 初始化 */this.initCanvas()},methods:{/** 初始化canvas */initCanvas(){const query = wx.createSelectorQuery().in(this)

      query
        .select('#canvas').fields({node:true,size:true,rect:true}).exec((res)=>{const ele = res[0]this.canvasEle = ele

          // 配置项const option ={ele:this.canvasEle,// canvas元素drawCallBack:this.draw,// 必须:用户自定义绘图方法scale:1,// 当前缩放倍数scaleStep:0.1,// 缩放步长(按钮)touchScaleStep:0.005,// 缩放步长(手势)maxScale:2,// 缩放最大倍数(缩放比率倍数)minScale:0.5,// 缩放最小倍数(缩放比率倍数)translate:{x:0,y:0},// 默认画布偏移isThrottleDraw:true,// 是否开启节流绘图(建议开启,否则安卓调用频繁导致卡顿)throttleInterval:20,// 节流绘图间隔,单位mspixelRatio: wx.getSystemInfoSync().pixelRatio,// 像素比(高像素比可以解决高清屏幕模糊问题)}this.canvasDraw =newCanvasDraw(option)// 创建CanvasDraw实例后就可以使用实例的所有方法了this.canvasDraw.draw()// 可以按实际需要调用绘图方法})},/** 用户自定义绘图内容 */draw(){// 默认绘图方式-圆形const{ ctx }=this.canvasDraw
      ctx.beginPath()
      ctx.strokeStyle ='#f00'
      ctx.arc(150,150,120,0,2* Math.PI)
      ctx.stroke()// 组件方法-绘制多边形const shapeOption ={points:[{x:127,y:347},{x:151,y:304},{x:173,y:344},{x:214,y:337},{x:184,y:396},{x:143,y:430},{x:102,y:400},],fillStyle:'#00f',}this.canvasDraw.drawShape(shapeOption)// 组件方法-绘制多线段const linesOption ={points:[{x:98,y:178},{x:98,y:212},{x:157,y:236},{x:208,y:203},{x:210,y:165},],strokeStyle:'#0f0',}this.canvasDraw.drawLines(linesOption)// 组件方法-绘制文字const textOption ={text:'组件方法-绘制文字',isCenter:true,point:{x:150,y:150},fillStyle:'#000',}this.canvasDraw.drawText(textOption)},/** 中心放大 */zoomIn(){this.canvasDraw.zoomIn()},/** 中心缩小 */zoomOut(){this.canvasDraw.zoomOut()},/** 重置画布(回复初始效果) */reset(){this.canvasDraw.reset()},/** 事件绑定 */tap(e){const p ={x:(e.detail.x -this.canvasEle.left)/this.canvasDraw.scale,y:(e.detail.y -this.canvasEle.top)/this.canvasDraw.scale,}
      console.log('点击坐标:', p)},touchstart(e){this.canvasDraw.touchstart(e)},touchmove(e){this.canvasDraw.touchmove(e)},touchend(e){this.canvasDraw.touchend(e)},},}</script><stylescoped>.canvas-wrap{position: relative;flex: 1;width: 100%;height: 100%;}.canvas{width: 100%;flex: 1;}</style>

3.2 核心类

canvasDraw.js

/**
 * @Author: 大话主席
 * @Description: 自定义小程序绘图类
 *//**
 * 绘图类
 * @param {object} option
 */exportfunctionCanvasDraw(option){if(!option.ele){
    console.error('canvas对象不存在')return}if(!option.drawCallBack){
    console.error('缺少必须配置项:drawCallBack')return}const{ ele }= option

  /** 外部可访问属性 */this.canvasNode = ele.node // wx的canvas节点this.canvasNode.width = ele.width // 设置canvas节点宽度this.canvasNode.height = ele.height // 设置canvas节点高度this.ctx =this.canvasNode.getContext('2d')this.zoomCenter ={x: ele.width /2,y: ele.height /2}// 缩放中心点this.touchMoveEvent =null// 触摸移动事件/** 内部使用变量 */let startPoint ={x:0,y:0}// 拖动开始坐标let startDistance =0// 拖动开始时距离(二指缩放)let curTranslate ={}// 当前偏移let curScale =1// 当前缩放let preScale =1// 上次缩放let drawTimer =null// 绘图计时器,用于节流let touchEndTimer =null// 触摸结束计时器,用于节流let fingers =1// 手指触摸个数/**
   * 根据像素比重设canvas尺寸
   */this.resetCanvasSize=()=>{this.canvasNode.width = ele.width *this.pixelRatio
    this.canvasNode.height = ele.height *this.pixelRatio
  }/**
   * 初始化
   */this.init=()=>{const optionCopy =JSON.parse(JSON.stringify(option))this.scale = optionCopy.scale ??1// 当前缩放倍数this.scaleStep = optionCopy.scaleStep ??0.1// 缩放步长(按钮)this.touchScaleStep = optionCopy.touchScaleStep ??0.005// 缩放步长(手势)this.maxScale = optionCopy.maxScale ??2// 缩放最大倍数(缩放比率倍数)this.minScale = optionCopy.minScale ??0.5// 缩放最小倍数(缩放比率倍数)this.translate = optionCopy.translate ??{x:0,y:0}// 默认画布偏移this.isThrottleDraw = optionCopy.isThrottleDraw ??true// 是否开启节流绘图(建议开启,否则安卓调用频繁导致卡顿)this.throttleInterval = optionCopy.throttleInterval ??20// 节流绘图间隔,单位msthis.pixelRatio = optionCopy.pixelRatio ??1// 像素比(高像素比解决高清屏幕模糊问题)

    startPoint ={x:0,y:0}// 拖动开始坐标
    startDistance =0// 拖动开始时距离(二指缩放)
    curTranslate =JSON.parse(JSON.stringify(this.translate))// 当前偏移
    curScale =this.scale // 当前缩放
    preScale =this.scale // 上次缩放
    drawTimer =null// 绘图计时器,用于节流
    fingers =1// 手指触摸个数this.resetCanvasSize()}this.init()/**
   * 绘图(会进行缩放和位移)
   */this.draw=()=>{this.clear()this.ctx.translate(this.translate.x *this.pixelRatio,this.translate.y *this.pixelRatio)this.ctx.scale(this.scale *this.pixelRatio,this.scale *this.pixelRatio)// console.log('当前位移', this.translate.x, this.translate.y, '当前缩放倍率', this.scale)
    option.drawCallBack()
    drawTimer =null}/**
   * 设置默认值(
   */this.setDefault=()=>{
    curTranslate.x =this.translate.x
    curTranslate.y =this.translate.y
    curScale =this.scale
    preScale =this.scale
  }/**
   * 清除画布(重设canvas尺寸会清空地图并重置canvas内置的scale/translate等)
   */this.clear=()=>{this.resetCanvasSize()}/**
   * 绘制多边形
   */this.drawShape=(opt)=>{this.ctx.beginPath()this.ctx.lineWidth ='1'this.ctx.fillStyle = opt.isSelect ? opt.HighlightfillStyle : opt.fillStyle
    this.ctx.strokeStyle = opt.HighlightStrokeStyle

    for(let i =0; i < opt.points.length; i++){const p = opt.points[i]if(i ===0){this.ctx.moveTo(p.x, p.y)}else{this.ctx.lineTo(p.x, p.y)}}this.ctx.closePath()if(opt.isSelect){this.ctx.stroke()}this.ctx.fill()}/**
   * 绘制多条线段
   */this.drawLines=(opt)=>{this.ctx.beginPath()this.ctx.strokeStyle = opt.strokeStyle
    for(let i =0; i < opt.points.length; i++){const p = opt.points[i]if(i ===0){this.ctx.moveTo(p.x, p.y)}else{this.ctx.lineTo(p.x, p.y)}}this.ctx.stroke()}/**
   * 绘制文字
   */this.drawText=(opt)=>{this.ctx.fillStyle = opt.isSelect ? opt.HighlightfillStyle : opt.fillStyle
    if(opt.isCenter){this.ctx.textAlign ='center'this.ctx.textBaseline ='middle'}this.ctx.fillText(opt.text, opt.point.x, opt.point.y)}/**
   * 重置画布(恢复到第一次绘制的状态)
   */this.reset=()=>{this.init()this.draw()}/**
   * 中心放大
   */this.zoomIn=()=>{this.zoomTo(this.scale +this.scaleStep)}/**
   * 中心缩小
   */this.zoomOut=()=>{this.zoomTo(this.scale -this.scaleStep)}/**
   * 缩放到指定倍数
   * @param {number} scale 缩放大小
   * @param {object} zoomCenter 缩放中心点(可选
   */this.zoomTo=(scale, zoomCenter0)=>{// console.log('缩放到:', scale, '缩放中心点:', zoomCenter0)this.scale = scale
    this.scale =this.scale >this.maxScale ?this.maxScale :this.scale
    this.scale =this.scale <this.minScale ?this.minScale :this.scale

    const zoomCenter = zoomCenter0 ||this.zoomCenter
    this.translate.x = zoomCenter.x -((zoomCenter.x -this.translate.x)*this.scale)/ preScale
    this.translate.y = zoomCenter.y -((zoomCenter.y -this.translate.y)*this.scale)/ preScale
    this.draw()
    preScale =this.scale
    curTranslate.x =this.translate.x
    curTranslate.y =this.translate.y
  }/**
   * 触摸开始
   */this.touchstart=(e)=>{
    fingers = e.touches.length
    if(fingers >2)returnthis.setDefault()// 单指if(fingers ===1){
      startPoint.x = e.touches[0].x
      startPoint.y = e.touches[0].y
    }elseif(fingers ===2){
      startDistance =this.get2PointsDistance(e)}}/**
   * 触摸移动
   */this.touchmove=(e)=>{if(fingers >2)returnif(this.isThrottleDraw){if(drawTimer)returnthis.touchMoveEvent = e
      drawTimer =setTimeout(this.touchmoveSelf,this.throttleInterval)}else{this.touchMoveEvent = e
      this.touchmoveSelf()}}/**
   * 触摸移动实际执行
   */this.touchmoveSelf=()=>{const e =this.touchMoveEvent
    // 单指移动if(fingers ===1){this.translate.x = curTranslate.x +(e.touches[0].x - startPoint.x)this.translate.y = curTranslate.y +(e.touches[0].y - startPoint.y)this.draw()}elseif(fingers ===2&& e.touches.length ===2){// 双指缩放const newDistance =this.get2PointsDistance(e)const distanceDiff = newDistance - startDistance
      const zoomCenter ={x:(e.touches[0].x + e.touches[1].x)/2,y:(e.touches[0].y + e.touches[1].y)/2,}this.zoomTo(curScale +this.touchScaleStep * distanceDiff, zoomCenter)}else{
      drawTimer =null}}/**
   * 触摸结束
   */this.touchend=()=>{if(this.isThrottleDraw){
      touchEndTimer =setTimeout(this.setDefault,this.throttleInterval)}else{this.setDefault()}}/**
   * 销毁
   */this.destroy=()=>{clearTimeout(drawTimer)clearTimeout(touchEndTimer)
    drawTimer =null
    touchEndTimer =nullthis.canvasNode =nullthis.ctx =nullthis.touchMoveEvent =null
    option.drawCallBack =null}/**
   * 获取2触摸点距离
   * @param {object} e 触摸对象
   * @returns 2触摸点距离
   */this.get2PointsDistance=(e)=>{if(e.touches.length <2)return0const xMove = e.touches[1].x - e.touches[0].x
    const yMove = e.touches[1].y - e.touches[0].y
    return Math.sqrt(xMove * xMove + yMove * yMove)}}exportdefault CanvasDraw
兄弟,如果帮到你,点个赞再走

本文转载自: https://blog.csdn.net/iamlujingtao/article/details/128289849
版权归原作者 iamlujingtao 所有, 如有侵权,请联系我们删除。

“小程序canvas 缩放/拖动/还原/封装和实例--开箱即用”的评论:

还没有评论