小程序canvas自定義圓弧進度條(不是顯示在最上層)---進化版

效果圖對比 上邊使用wx.createCanvasContext('circle'),canva畫出來的會顯示在最上層,下邊使用wx.createSelectorQuery()canvas畫出來會顯示正常

如果想在使用canvas做的自定義組件的當前頁面要顯示彈出框或者是蒙層,wx.createCanvasContext('circle')畫出來的永遠都是顯示在頁面最上層,wx.createSelectorQuery()畫出來的顯示正常

如果在頁面中使用canvas,使用wx.createSelectorQuery()
如果是自定義組件使用canvas,使用wx.createSelectorQuery().in(this)

image.png

效果圖

image.png

思路

考慮到背景是不變的,把背景和會動的分為兩個canvas來進行畫


image.png

image.png

wxss

page{
  background: #fd7895;
}

.component-slider {
  width: 90%;
  position: relative;
  margin: 0rpx auto 0rpx;
  padding: 70rpx 0 70rpx;
}

/* 盒子 */
.slider-box {
  width: 88%;
  margin: 0 auto;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

/* 未選中區(qū)線 */
.slider-line {
  width: 100%;
  height: 10rpx;
  background: rgba(91, 150, 246, 0.1);
  margin: 0 auto;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

/* 選中區(qū)線 */
.slider-line-active {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translate(0, -50%);
  height: 10rpx;
  background: #5B96F6;
}

/* slider按鈕 */
.slider-btn {
  width: 70rpx;
  height: 35rpx;
  background: #5B96F6;
  border-radius: 20rpx;
}

/* 顯示的數(shù)字 */
.slider-number {
  width: 100%;
  position: absolute;
  bottom: -10rpx;
}

.slider-number text {
  position: absolute;
  top: 0;
  font-size: 24rpx;
  color: #999999;
  transition: all 0.3s;
}

/* 當前選中的數(shù)字 */
.slider-number text.active {
  font-size: 32rpx;
  color: #5B96F6;
  transition: all 0.3s;
}

/* slider組件設(shè)置透明 */
slider {
  opacity: 0;
}

wxml


<view style="position: relative;margin-top: 100rpx;">
  <canvas id="circle" type="2d" style="border: 1rpx solid red;width: 90vw;height: 90vw;margin: 0 auto;"></canvas>
  <canvas id="circle2" type="2d" style="border: 1rpx solid green;width: 90vw;height: 90vw;margin: 0 auto;position: absolute;top: 0;right: 0;bottom: 0;left: 0;z-index: 10;"></canvas>
</view>
<view style="display: flex;flex-direction: row;margin-top: 50rpx;padding: 0 40rpx;">
  <button bindtap="btnHandler" data-type="{{1}}" type="default" style="margin-right: 50rpx;">加+1</button>
  <button bindtap="btnHandler" data-type="{{2}}" type="default">減-1</button>
</view>
<view style="display: flex;flex-direction: row;margin-top: 50rpx;padding: 0 40rpx;">
  <button bindtap="clickHandler" data-type="{{1}}" type="default" style="margin-right: 50rpx;">開始</button>
  <button bindtap="clickHandler" data-type="{{2}}" type="default">結(jié)束</button>
</view>

js

const app = getApp();

Page({
  data: {
    min: 0, // 最小限制 
    max: 5, // 最大限制
    value: 60, // 當前value
    zu: [],
    addId: null,
    reduceId: null,
    centerPoint: 0,
    radius: 0,
    whiteRadius: 0,
    subCircleth: 0,
    sexth: 0,
    ctx: null,
    ctx2: null,
    canvas: null,
  },
  onLoad(option) {
    this.initData();
  },
  onShow() {},
  initData() {
    const width = app.globalData.width * 0.9;
    const height = app.globalData.width * 0.9;
    this.data.radius = width / 2 - 80;
    this.data.centerPoint = { //中心點
      x: width / 2,
      y: height / 2
    };
    this.data.whiteRadius = this.data.radius * 0.7;
    this.data.subCircleth = this.data.radius * 0.3;
    this.data.sexth = this.data.radius * 0.05;

    const query = wx.createSelectorQuery();
    query.select('#circle')
      .fields({
        node: true,
        size: true
      })
      .exec(res => {
        const canvas = res[0].node; // Canvas 對象
        this.data.ctx = canvas.getContext('2d');

        // 初始化畫布大小
        const dpr = wx.getSystemInfoSync().pixelRatio;
        canvas.width = width * dpr; // 初始化畫布大小--寬
        canvas.height = height * dpr; // 初始化畫布大小--高
        this.data.ctx.scale(dpr, dpr);
        this.data.ctx.translate(this.data.centerPoint.x, this.data.centerPoint.y);
        this.canvasCircle();
      });

    const query2 = wx.createSelectorQuery();
    query2.select('#circle2')
      .fields({
        node: true,
        size: true
      })
      .exec(res => {
        this.data.canvas = res[0].node;
        this.data.ctx2 = this.data.canvas.getContext('2d');

        const dpr = wx.getSystemInfoSync().pixelRatio;
        this.data.canvas.width = width * dpr;
        this.data.canvas.height = height * dpr;
        this.data.ctx2.scale(dpr, dpr);
        this.data.ctx2.translate(this.data.centerPoint.x, this.data.centerPoint.y);
        this.proCanvasCircle();
      });
  },

  btnHandler(e) {
    const type = e.currentTarget.dataset.type;
    let value = this.data.value;
    if (type == 1) {
      value++;
      if (value > 150) return;
    } else {
      value--;
      if (value < 0) return;
    }
    this.data.value = value;
    this.proCanvasCircle();
  },
  // 拖動過程中觸發(fā)的事件
  sliderchanging(e) {
    var value = e.detail.value;
    this.setData({
      value: value
    })
  },
  clickHandler(e) {
    const type = e.currentTarget.dataset.type;
    if (type == 1) {
      clearInterval(this.data.startId);
      clearInterval(this.data.reduceId);
      this.addNum();
    } else {
      clearInterval(this.data.addId);
      clearInterval(this.data.reduceId);
    }
  },
  addNum() {
    this.data.addId = setInterval(() => {
      let value = this.data.value;
      value++;
      if (value > 150) {
        clearInterval(this.data.addId);
        this.recuceNum();
      } else {
        this.data.value = value;
        this.proCanvasCircle();
      }
    }, 100);
  },
  recuceNum() {
    this.data.reduceId = setInterval(() => {
      let value = this.data.value;
      value--;
      if (value < 0) {
        clearInterval(this.data.reduceId);
        this.addNum();
      } else {
        this.data.value = value;
        this.proCanvasCircle();
      }
    }, 100);
  },
  test() {
    for (let index = 0; index < 20; index++) {
      this.data.zu.push(index);
    }
  },
  // 完成一次拖動后觸發(fā)的事件
  sliderchange(e) {
    var value = e.detail.value;
    this.setData({
      value: value
    });
  },
  proCanvasCircle() {
    const value = this.data.value;

    // 繪制前清空之前的畫布
    this.data.ctx2.clearRect(-this.data.centerPoint.x, -this.data.centerPoint.y,
      this.data.centerPoint.x * 2, this.data.centerPoint.y * 2);


    //當前值
    this.data.ctx2.font = '22px sans-serif';
    const valueStr = '' + value;
    const valueWidth = this.data.ctx2.measureText(valueStr).width;
    this.data.ctx2.beginPath();
    this.data.ctx2.fillStyle = 'black';
    this.data.ctx2.fillText(valueStr, -valueWidth, -this.data.radius * 0.02);
    this.data.ctx2.fill();
    this.data.ctx2.stroke();

    this.data.ctx2.rotate(180 * Math.PI / 180); //旋轉(zhuǎn)180,起點從9點鐘開始
    //進度色圓環(huán)
    this.data.ctx2.lineWidth = this.data.subCircleth;
    this.data.ctx2.lineCap = 'butt';
    this.data.ctx2.beginPath();
    this.data.ctx2.strokeStyle = '#fbc850';
    this.data.ctx2.arc(0, 0, this.data.whiteRadius + this.data.subCircleth / 2, 0 * Math.PI, value / 150 * 1.25 * Math.PI);
    this.data.ctx2.stroke();

    //進度圓點
    const pointRadius = this.data.radius * 0.86;
    const ponitPosition = {};
    const bili = 30 / 45; // 每一值代表多度
    const circleWidth = this.data.radius * 0.13;
    this.data.ctx2.beginPath();
    if (value >= 0 && value <= 90) { //從0mmHg~90mmHg之間的夾角  0°~135°
      ponitPosition.x = -Math.cos(value / bili * Math.PI / 180) * pointRadius;
      ponitPosition.y = -Math.sin(value / bili * Math.PI / 180) * pointRadius;
    } else if (value > 90 && value <= 120) { //從90mmHg~120mmHg之間的夾角 0°~45°
      ponitPosition.x = Math.cos((45 - (value - 90) / bili) * Math.PI / 180) * pointRadius;
      ponitPosition.y = -Math.sin((45 - (value - 90) / bili) * Math.PI / 180) * pointRadius;
    } else if (value >= 120 && value <= 150) { //從120mmHg~150mmHg之間的夾角 0°~45°
      ponitPosition.x = Math.cos((value - 120) / bili * Math.PI / 180) * pointRadius;
      ponitPosition.y = Math.sin((value - 120) / bili * Math.PI / 180) * pointRadius;
    }
    const image = this.data.canvas.createImage();
    image.onload = () => {
      this.data.ctx2.drawImage(
        image,
        ponitPosition.x - circleWidth / 2, ponitPosition.y - circleWidth / 2,
        circleWidth, circleWidth
      )
    }
    image.src = '../../image/circle.png'

    //最外側(cè)有色圓環(huán)
    this.data.ctx2.beginPath();
    this.data.ctx2.lineWidth = this.data.sexth;
    this.data.ctx2.lineCap = 'round';
    this.data.ctx2.strokeStyle = '#fbc850';
    this.data.ctx2.fill();
    this.data.ctx2.arc(0, 0, this.data.whiteRadius + this.data.subCircleth + this.data.sexth, 0 * Math.PI, value / 150 * 1.25 * Math.PI);
    this.data.ctx2.stroke();

    this.data.ctx2.rotate(-180 * Math.PI / 180); //旋轉(zhuǎn)180,起點從9點鐘開始

  },
  canvasCircle() {
    //中間白色圓
    this.data.ctx.beginPath();
    this.data.ctx.strokeStyle = 'white';
    this.data.ctx.arc(0, 0, this.data.whiteRadius, 0, 2 * Math.PI);
    this.data.ctx.fillStyle = 'white'
    this.data.ctx.fill();
    this.data.ctx.stroke();

    //半透明圓環(huán)
    this.data.ctx.lineWidth = this.data.subCircleth;
    this.data.ctx.beginPath();
    this.data.ctx.strokeStyle = 'white';
    this.data.ctx.globalAlpha = 0.5; //設(shè)置為半透明
    this.data.ctx.arc(0, 0, this.data.whiteRadius + this.data.subCircleth / 2, 0, 2 * Math.PI);
    this.data.ctx.stroke();
    this.data.ctx.globalAlpha = 1; //設(shè)置為不透明

    this.data.ctx.rotate(90 * Math.PI / 180); //旋轉(zhuǎn)-90,起點從9點鐘開始
    const singeAngle = 15;
    const lineLength = 10;
    const lineCount = 225 / 15 + 1;
    for (let index = 0; index < lineCount; index++) {
      const ponit = {
        x: 0,
        y: this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 2
      };
      const arrowPoint = {
        x: 0,
        y: this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength
      }
      if (index == 1 || index == 2 || index == 4 || index == 5 || index == 7 || index == 8 ||
        index == 10 || index == 11 || index == 13 || index == 14) {
        ponit.y = this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength;
        arrowPoint.y = this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength;
      }
      this.data.ctx.strokeStyle = 'white';
      this.data.ctx.lineWidth = 2;
      this.data.ctx.beginPath();
      this.data.ctx.moveTo(ponit.x, ponit.y);
      this.data.ctx.lineTo(arrowPoint.x, arrowPoint.y);
      this.data.ctx.stroke();

      this.data.ctx.rotate(singeAngle * Math.PI / 180);
    }

    this.data.ctx.rotate(-(singeAngle * lineCount + 90) * Math.PI / 180); //旋轉(zhuǎn)-singeAngle * lineCount,起點從9點鐘開始

    //畫0 mmHg
    const unitStr = 'mmHg';
    const onetStr = '0';
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '14px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(onetStr, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 4), 0);
    this.data.ctx.stroke();
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '10px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(unitStr, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 4) * 1.18, this.data.sexth * 2.5);
    this.data.ctx.stroke();

    //畫30 mmHg
    const twotStr = '30';
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '14px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(twotStr, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 4) * 0.8, -Math.sin(45) * this.data.radius * 1.1);
    this.data.ctx.stroke();
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '10px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(unitStr, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 4) * 0.91, -Math.sin(45) * this.data.radius * 0.95);
    this.data.ctx.stroke();

    //畫60 mmHg
    const threetStr = '60';
    const threeStrWidth = this.data.ctx.measureText(threetStr).width;
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '14px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(threetStr, -threeStrWidth, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength) * 1.2);
    this.data.ctx.stroke();
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '10px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(unitStr, threeStrWidth / 2, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength) * 1.2);
    this.data.ctx.stroke();

    //畫90 mmHg
    const fourStr = '90';
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '14px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(fourStr, this.data.radius, -this.data.radius * 0.9);
    this.data.ctx.stroke();
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '10px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(unitStr, this.data.radius, -this.data.radius * 0.8);
    this.data.ctx.stroke();

    //畫120 mmHg
    const fiveStr = '120';
    const fiveStrWidth = this.data.ctx.measureText(fiveStr).width;
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '14px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(fiveStr, this.data.radius * 1.35, 0);
    this.data.ctx.stroke();
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '10px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(unitStr, this.data.radius * 1.35, fiveStrWidth * 0.65);
    this.data.ctx.stroke();

    //畫150 mmHg
    const sixStr = '150';
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '14px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(sixStr, this.data.radius * 0.95, this.data.radius * 1.1);
    this.data.ctx.stroke();
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'white';
    this.data.ctx.font = '10px sans-serif';
    this.data.ctx.lineWidth = 0.5;
    this.data.ctx.fill();
    this.data.ctx.fillText(unitStr, this.data.radius * 0.95, this.data.radius * 1.23);
    this.data.ctx.stroke();

    //中間mmHg
    this.data.ctx.font = '10px sans-serif';
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'black';
    this.data.ctx.fillText(unitStr, 0, 0);
    this.data.ctx.fill();
    this.data.ctx.stroke();

    //實時壓力
    this.data.ctx.font = '15px sans-serif';
    const tipStr = '實時壓力';
    const tipWidth = this.data.ctx.measureText(tipStr).width;
    this.data.ctx.beginPath();
    this.data.ctx.fillStyle = 'black';
    this.data.ctx.fillText(tipStr, -tipWidth / 2, this.data.radius * 0.2);
    this.data.ctx.fill();
    this.data.ctx.stroke();

    //圓心
    // this.data.ctx.lineWidth = 5;
    // this.data.ctx.beginPath();
    // this.data.ctx.strokeStyle = 'green';
    // this.data.ctx.arc(0, 0, 2, 0, 2 * Math.PI);
    // this.data.ctx.stroke();

  }
})
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容