首页 > 编程语言 >微信小程序canvas2d实现可滑动的圆环形进度条

微信小程序canvas2d实现可滑动的圆环形进度条

时间:2024-04-23 11:13:47浏览次数:32  
标签:canvas const 进度条 微信 ctx canvas2d data Math

 

最近在搞一个微信小程序,有一个圆环的进度条,而且要求颜色要渐变的,本来想用秋云插件实现,但是秋云的插件不能滑动这个进度条,后面用canvas实现

成品效果图:

避坑:  

<canvas  id="myCanvas" type="2d"></canvas>

<canvas canvas-id="myCanvas"  ></canvas>

两个canvas标签,一个是新版的,一个是旧版的,如果指定了type,那么必须要指定id属性值,不然wx.createSelectorQuery()是无法获取到标签的

如果不指定type,使用旧版代码 wx.createCanvasContext('myCanvas')实现,会出现层级问题,canvas标签永远在最顶层,而且是无解的, 所以使用新的canvas2d去实现项目需求。

 

新canvas的宽高默认是300*150的,这个值不会根据css样式改变,需要手动设置

wx.createSelectorQuery()
    .select('#myCanvas').fields({ node: true, size: true })
    .exec((res) => {
      const canvas = res[0].node
      console.log(canvas.width, canvas.height);
    
    canvas.width = 250     canvas.height = 250
    })

 

新版的canvas使用了web3C标准,推荐文档:https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D

实现代码:

js代码 

Page({
  /**
   * 页面的初始数据
   */
  data: {
    progress: 74, // 当前转盘值
    isDragging: false, // 是否触摸中
    startAngle: -0.5 * Math.PI, // 开始角度
    endAngle: -0.5 * Math.PI, // 结束角度
    radius: 100, // 圆环半径
    centerX: 0,
    centerY: 0
  },
onReady() {
    this.setData({
      centerX: 250 / 2,
      centerY: 250 / 2
    })
    // 计算圆盘的初始值
    let endAngleVal:number = (this.data.progress - 25) / (5) * 0.1
    this.setData({
      endAngle: endAngleVal * Math.PI
    })
    setTimeout(() => {
      this.drawCircle();
    }, 200)
  },
drawCircle() {
    wx.createSelectorQuery()
    .select('#myCanvas').fields({ node: true, size: true })
    .exec((res) => {
      const canvas = res[0].node
      canvas.width = 250
      canvas.height = 250
      const ctx = canvas.getContext('2d')
      ctx.clearRect(0, 0, canvas.width, canvas.height) // 清除画布
      ctx.moveTo(0, 0.5); // 使用0.5增量对齐像素,消除锯齿
      // 背景圆弧渲染
      ctx.beginPath();
      ctx.arc(this.data.centerX, this.data.centerY, this.data.radius, 0, 2 * Math.PI);
      ctx.strokeStyle = '#a6a6a6'; // 设置描边颜色  
      ctx.lineWidth = 20; // 设置描边宽度  
      ctx.stroke(); 
      // 进度条渲染
      ctx.beginPath();
      ctx.arc(this.data.centerX, this.data.centerY, this.data.radius, this.data.startAngle, this.data.endAngle, false); 
      // 渐变颜色
      const gradient = ctx.createConicGradient(-10, 125, 125);
      // const gradient = ctx.createLinearGradient(100, 0, 125, 125);
      gradient.addColorStop(0, "#00a8e3");
      gradient.addColorStop(0.5, "#fde001");
      gradient.addColorStop(1, "#f00");
      // ctx.strokeStyle = '#007BFF'; // 描边颜色  
      ctx.strokeStyle = gradient;
      ctx.lineWidth = 20; // 设置描边宽度  
      ctx.lineCap = 'round' // 线条类型
      ctx.stroke(); // 描边路径,绘制环形进度条  

      // 滑动圆点
      ctx.beginPath();
      let whitePoint = {
        x: this.data.centerX + this.data.radius * Math.cos(this.data.endAngle),
        y: this.data.centerY + this.data.radius * Math.sin(this.data.endAngle)
      };
            ctx.strokeStyle = '#FFF'
            ctx.lineCap = 'round';
            ctx.lineWidth = 8;
            ctx.arc(whitePoint.x, whitePoint.y, 6, 0, 2 * Math.PI); // 空心圆
            ctx.stroke();
      ctx.closePath(); // 结束画布路径
    })
  },
updateAngle(x:number, y:number) {
    const dx = x - this.data.centerX;
    const dy = y - this.data.centerY;
    const angle = Math.atan2(dy, dx); // 计算触摸点与中心点的连线与x轴的夹角
    if(angle > -1.778 && angle < -1.56) {
      return
    }
    let progressVal = (angle / (2 * Math.PI) + 0.5) * 100
    let progressRange = parseFloat(Math.max(0, Math.min(100, progressVal)).toFixed(0)) 
    if (progressRange > 25) {
      progressRange -= 25
    } else {
      progressRange += 75
    }
    if (progressRange >= 100) {
      progressRange = 0
    }
    this.setData({
      endAngle : angle,
      // 确保进度在0到100之间  
      progress : progressRange
    })
  },
  doTouchStart(e:any) {//触摸开始
    this.setData({
      isDragging : true
    })
    this.updateAngle(e.touches[0].x, e.touches[0].y);
    this.drawCircle();
  },
  doTouchMove (e:any) {//触摸移动
    if (this.data.isDragging) {
      this.updateAngle(e.touches[0].x, e.touches[0].y);
      this.drawCircle();
    }
  },
doTouchend() {//触摸结束
    this.setData({
      isDragging : false
    })
  },
}

 

 

 wxml代码

<canvas  id="myCanvas" type="2d"  disable-scroll="{{true}}" bindtouchmove="doTouchMove" bindtouchstart="doTouchStart" bindtouchend="doTouchend" 
bindtouchcancel="doTouchend" class="canvas-style"></canvas>
<text>{{progress}}</text>

 

在微信开发者工具中显示正常,但是仍有问题

官网中说canvas2D是支持全部的web标准了,确实在开发者工具中没有错误,但是真机调试中有错误

 createConicGradient 这个方法报错了,这个方法是渐变颜色,圆椎形态渐变,很适合做圆环的渐变,

var ctx = canvas.getContext('2d');
                ctx.beginPath();
                const gradient = ctx.createConicGradient(-1.569, 200, 200);
                gradient.addColorStop(0, "#00a8e3");
                gradient.addColorStop(0.5, "#fde001aa");
                gradient.addColorStop(1, "#f00");
                ctx.fillStyle = gradient;
                ctx.fillRect(0, 0, 500, 500);
                ctx.closePath();

 

效果图:

奈何微信小程序不支持,后面我有想到了放这张锥形渐变图片上去,使用createPattern方法,先在html中试过了可以实现

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>可滑动的环形进度条</title>
        <style>
            canvas {
                border: 1px solid black;
            }
        </style>
    </head>
    <body>
        <canvas id="progressCanvas" width="300" height="300"></canvas>
        <style>
            #progressCanvas {
                /* background: linear-gradient(45deg, #ff7e5f, #feb47b, #fef); */
            }
        </style>
        <script>
            const canvas = document.getElementById('progressCanvas');
            const ctx = canvas.getContext('2d');

            let progress = 0; // 进度值,范围从0到100  
            let isDragging = false; // 是否正在拖动进度条  
            let startAngle = -0.5 * Math.PI; // 起始角度  
            let endAngle = startAngle; // 结束角度  
            const radius = 100; // 进度条半径  
            const centerX = canvas.width / 2; // 进度条中心X坐标  
            const centerY = canvas.height / 2; // 进度条中心Y坐标  

            // 绘制环形进度条  
            function drawRingProgress() {
                ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除画布  

                // 背景圆弧
                ctx.beginPath();
                ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
                ctx.strokeStyle = '#a6a6a6'; // 设置描边颜色  
                ctx.lineWidth = 20; // 设置描边宽度  
                ctx.stroke(); // 描边路径,绘制环形进度条  

                // 进度颜色
                ctx.beginPath();
                ctx.arc(centerX, centerY, radius, startAngle, endAngle, false); // 绘制环形进度条的路径  

                // 替换成渐变颜色
                let iamge = new Image()
                iamge.src = './a.png'
                // ctx.strokeStyle = '#007BFF'; // 设置描边颜色  
                ctx.strokeStyle = ctx.createPattern(iamge, "no-repeat")
                ctx.lineWidth = 20; // 设置描边宽度  
                ctx.lineCap = 'round'
                ctx.stroke(); // 描边路径,绘制环形进度条  

                // 滑动圆点
                ctx.beginPath();
                let whitePoint = {
                    x: centerX + radius * Math.cos(endAngle),
                    y: centerY + radius * Math.sin(endAngle)
                };

                ctx.strokeStyle = '#FFF'
                ctx.lineCap = 'round';
                ctx.lineWidth = 8;
                ctx.arc(whitePoint.x, whitePoint.y, 6, 0, 2 * Math.PI); // 空心圆
                ctx.stroke();
                ctx.closePath();
            }

            // 更新进度条的角度  
            function updateAngle(x, y) {
                const dx = x - centerX;
                const dy = y - centerY;
                const angle = Math.atan2(dy, dx); // 计算触摸点与中心点的连线与x轴的夹角  
                endAngle = angle;
                progress = (angle / (2 * Math.PI) + 0.5) * 100; // 将角度转换为百分比进度  
                progress = Math.max(0, Math.min(100, progress)); // 确保进度在0到100之间  
            }

            // 监听鼠标按下事件  
            canvas.addEventListener('mousedown', (e) => {
                isDragging = true;
                updateAngle(e.clientX, e.clientY);
                drawRingProgress();
            });

            // 监听鼠标移动事件  
            canvas.addEventListener('mousemove', (e) => {
                if (isDragging) {
                    updateAngle(e.clientX, e.clientY);
                    drawRingProgress();
                }
            });

            // 监听鼠标松开事件  
            canvas.addEventListener('mouseup', () => {
                isDragging = false;
            });

            // 监听鼠标离开画布事件  
            canvas.addEventListener('mouseleave', () => {
                isDragging = false;
            });

            // 初始绘制  
            drawRingProgress();
        </script>
    </body>
</html>

 

效果图

 后面再到微信小程序这边,我才想起来,微信小程序是没有 Image() 这个方法的,获取不到图片对象,没有办法通过图片去实现。

所以还是用了createLinearGradient方法。希望微信后续可以修改好这个问题。

 

标签:canvas,const,进度条,微信,ctx,canvas2d,data,Math
From: https://www.cnblogs.com/yangWanSheng/p/18152254

相关文章

  • 【JavaScript】微信小程序:高效性能优化策略与实践
    ​本文作者:黄启聪,碧桂园服务前端开发高级工程师,专注于运用前沿的Web技术提升工作效率,并致力于打造卓越的交互式用户体验。​01前言目前,凤凰会商城支持全国商城、门店、酒司令、地推、群接龙等多种业务,并且具备多端能力。一套代码可以在凤凰会APP、移动端H5和微信小程序中运行......
  • js逆向实战之微信公众平台pwd参数解密
    网址:https://mp.weixin.qq.com/分析过程调出开发者工具,切换到network模块,随便输入一个账号和密码,抓包。看到pwd参数被加密了,接下来就是去找到pwd参数的加密过程。但其实这里可以进行加密方法的猜测,密码处输入123456,抓包。相信有经验的人可以很快的反应出是个md5算法。其......
  • mumu模拟器上微信安装目录缓存目录
    前言全局说明mumu模拟器上微信安装目录缓存目录一、微信安装目录/storage/emulated/0/Android/data/com.tencent.mm二、缓存目录/data/data/com.tencent.mm/cache/<一串32位数字>/finder/video三、四、免责声明:本号所涉及内容仅供安全研究与教学使用,如出现......
  • 2024-04-22 微信小程序开发之ios输入框光标指向不正确
    业务场景:微信小程序开发中,页面有一个固定在底部(实测只要是设置了固定的输入框都会这样,如果可以改变布局,不固定定位则可避免此问题)的输入框,点击输入框输入文字,操作流程完成。但是!在神奇的ios系统中却不行,就是点击输入框,你是无法触发到输入框的,说白了点不到,安卓就没有这毛病。原......
  • 02_微信小程序常用的方法封装
     消息提示框封装://消息提示框封装/**//提示的内容title:'消息提示框',//提示的内容icon:'success',//提示的图标,success(成功)、error(失败)、loading(加载)、none(不显示图标)duration:2000,//提示的延迟时间mask:true//是否显示透......
  • 微信小程序功能-----基础轮播图配置
    使用swiper设置轮播图,下面是实现效果图页面结构<swiperclass="content-info-slide"indicator-color="rgba(255,255,255,.5)"indicator-active-color="#fff"indicator-dotscircularautoplay><swiper-item><imagesrc="/page......
  • 4 22微信小程序商品浏览
     需要四个接口:     ......
  • 微信小程序的表单验证
    在项目中用到过的稍有改动的WxValidate.js文件/***表单验证**@param{Object}rules验证字段的规则*@param{Object}messages验证字段的提示信息**/classWxValidate{constructor(rules={},messages={}){Object.assign(this,{data:{......
  • 3月30日成功举办——基于实在智能RPA的微信私域自动化运营公开课
    3月30日,RPA学习天地成功举办了,基于微信私域运营的RPA自动化直播公开课,成功地帮助学员们普及了社交化工具私域运营的自动化技巧。 本次公开课,主要介绍了如何使用实在智能RPA工具来自动化实现微信的好友添加,建群,群发信息等私域互动信息,受到了观众的好评。以下为公开课部分精彩......
  • 微信登录+商品浏览
    先解决了图片不能回显的问题:以前在学习springboot3+vue3的时候当时的配置是  这样配置可以上传图片。然后学外卖的时候还用这个配置,发现不能图片不能回显了,把选中部分删了就可以图片回显了,我只能说,抽象。   为什么要使用HttpClient,因为在实现微信登录的过程中呢,需......