WeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

青灯夜游
リリース: 2022-03-08 20:02:55
転載
8052 人が閲覧しました

WeChat アプレットで天気折れ線グラフを描画するにはどうすればよいですか?次の記事では、キャンバスを使用して WeChat アプレットで天気折れ線グラフを描画し、3 次ベジェ曲線を使用して温度ポイントをフィッティングして滑らかにし、曲線の下部に背景色を付ける方法を紹介します。 . 皆様のお役に立てれば幸いです!

WeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

ポリライン

レンダリング:

WeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

##カスタム コンポーネントの折れ線グラフ

<canvas type="2d" id="line" class="line-class"   style="max-width:90%" />
ログイン後にコピー
Component({
  externalClasses: [&#39;line-class&#39;],
  properties: {
    width: String,
    height: String,
    data: Array,
  },
  observers: {
    width() {
      // 这里监听 width 变化重绘 canvas
      // 动态传入 width 好像只能这样了..
      const query = this.createSelectorQuery();
      query
        .select(&#39;#line&#39;)
        .fields({ node: true, size: true })
        .exec(res => {
          const canvas = res[0].node;
          const ctx = canvas.getContext(&#39;2d&#39;);
          const width = res[0].width; // 画布宽度
          const height = res[0].height; // 画布高度

          console.log(`宽度: ${width}, 高度: ${height}`);

          const dpr = wx.getSystemInfoSync().pixelRatio;
          canvas.width = width * dpr;
          canvas.height = height * dpr;
          ctx.scale(dpr, dpr);

          // 开始绘图
          this.drawLine(ctx, width, height, this.data.data);
        });
    },
  },
  methods: {
    drawLine(ctx, width, height, data) {
      const Max = Math.max(...data);
      const Min = Math.min(...data);

      // 把 canvas 的宽度, 高度按一定规则平分
      const startX = width / (data.length * 2), // 起始点的横坐标 X
        baseY = height * 0.9, // 基线纵坐标 Y
        diffX = width / data.length,
        diffY = (height * 0.7) / (Max - Min); // 高度预留 0.2 写温度

      ctx.beginPath();
      ctx.textAlign = &#39;center&#39;;
      ctx.font = &#39;13px Microsoft YaHei&#39;;
      ctx.lineWidth = 2;
      ctx.strokeStyle = &#39;#ABDCFF&#39;;

      // 画折线图的线
      data.forEach((item, index) => {
        const x = startX + diffX * index,
          y = baseY - (item - Min) * diffY;

        ctx.fillText(`${item}°`, x, y - 10);
        ctx.lineTo(x, y);
      });
      ctx.stroke();

      // 画折线图背景
      ctx.lineTo(startX + (data.length - 1) * diffX, baseY); // 基线终点
      ctx.lineTo(startX, baseY); // 基线起点
      const lingrad = ctx.createLinearGradient(0, 0, 0, height * 0.7);
      lingrad.addColorStop(0, &#39;rgba(255,255,255,0.9)&#39;);
      lingrad.addColorStop(1, &#39;rgba(171,220,255,0)&#39;);
      ctx.fillStyle = lingrad;
      ctx.fill();

      // 画折线图上的小圆点
      ctx.beginPath();
      data.forEach((item, index) => {
        const x = startX + diffX * index,
          y = baseY - (item - Min) * diffY;

        ctx.moveTo(x, y);
        ctx.arc(x, y, 3, 0, 2 * Math.PI);
      });
      ctx.fillStyle = &#39;#0396FF&#39;;
      ctx.fill();
    },
  },
});
ログイン後にコピー

data は [1, 2, ...] などの温度配列です。

温度がいくつあるかわからないためです。値があるため、ここの幅は動的に渡されます。

小さな問題があります。つまり、幅が大きすぎると、実際のマシンでは表示されません...

 // 获取 scroll-view 的总宽度
 wx.createSelectorQuery()
      .select(&#39;.hourly&#39;)
      .boundingClientRect(rect => {
        this.setData({
          scrollWidth: rect.right - rect.left,
        });
      })
      .exec();
ログイン後にコピー
<view class="title">小时概述</view>
<scroll-view scroll-x scroll-y class="scroll" show-scrollbar="{{false}}" enhanced="{{true}}">
    <view class="hourly">
      <view wx:for="{{time}}" wx:key="index">{{item}}</view>
    </view>
    <line-chart line-class="line" width="{{scrollWidth}}" height="100" data="{{temp}}" />
</scroll-view>
ログイン後にコピー

ここにscroll-xとscroll-yを書きます。そうする必要があります。絶対位置オフセットの問題はありません。理由はわかりません。

WeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

.scroll {
  position: relative;
  height: 150px;
  width: 100%;
}

.hourly {
  display: flex;
  height: 150px;
  position: absolute;
  top: 0;
}

.hourly > view {
  min-width: 3.5em;
  text-align: center;
}

.line { // 折线图绝对定位到底部
  position: absolute;
  bottom: 0;
}
ログイン後にコピー

ここで使用される絶対位置は、実際には、ブロック内の門司天気と毎日のような折れ線グラフをシミュレートするためです。内部の効果なので、毎時はスクロールビューと同じ高さにする必要があり、キャンバスを配置する必要があります

主にMoji Weather がどのように実装されているかわからないので、これは一時的にしかできません

WeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

3 次ベジェ曲線

レンダリング

WeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

えっと、あまりスムーズとは思えません

コントロール ポイントの計算

最初にポイント クラスを作成します

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}
ログイン後にコピー

キャンバス ベジェ曲線描画ツール (karlew.com)

http://wx.karlew.com/canvas/bezier/

3 次ベジェ曲線の各パラメータの意味は、上記の Web サイトで知ることができます。

WeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

つまり、bezierCurveTo を使用する場合、最後の点は次の点、最初の 2 つは制御点です。

制御点の計算リファレンス: ベジェ曲線制御点決定方法 - Baidu Library

https://wenku. baidu.com/view/c790f8d46bec0975f565e211.html

#要約すると次のようになります

#a と b には任意の正の数を指定できますWeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

それでは、ある点の制御点 A と B を計算するメソッドを定義します

/**
 * 计算当前点的贝塞尔曲线控制点
 * @param {Point} previousPoint: 前一个点
 * @param {Point} currentPoint: 当前点
 * @param {Point} nextPoint1: 下一个点
 * @param {Point} nextPoint2: 下下个点
 * @param {Number} scale: 系数
 */
calcBezierControlPoints(
  previousPoint,
  currentPoint,
  nextPoint1,
  nextPoint2,
  scale = 0.25
) {
  let x = currentPoint.x + scale * (nextPoint1.x - previousPoint.x);
  let y = currentPoint.y + scale * (nextPoint1.y - previousPoint.y);

  const controlPointA = new Point(x, y); // 控制点 A

  x = nextPoint1.x - scale * (nextPoint2.x - currentPoint.x);
  y = nextPoint1.y - scale * (nextPoint2.y - currentPoint.y);

  const controlPointB = new Point(x, y); // 控制点 B

  return { controlPointA, controlPointB };
}
ログイン後にコピー

ここでのスケールは a と b ですが、それらの値は等しいです

ただし、最初の点にはpreviousPointがなく、最後から2番目のポイントにはnextPoint2がない

したがって、ポイントが最初の場合は、previousPointの代わりにcurrentPointを使用します

最後から2番目のポイントの場合は、nextPoint2の代わりにnextPoint1を使用します

最後の点については、bezierCurveTo の 3 番目のパラメータが次のパラメータであるため、何もする必要はありません。座標を指定するだけで点を接続できます。制御点を計算する必要はありません WeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)

したがって、3 次ベジェ曲線を描画する方法:

/**
 * 绘制贝塞尔曲线
 * ctx.bezierCurveTo(控制点1, 控制点2, 当前点);
 */
drawBezierLine(ctx, data, options) {
  const { startX, diffX, baseY, diffY, Min } = options;

  ctx.beginPath();
  // 先移动到第一个点
  ctx.moveTo(startX, baseY - (data[0] - Min) * diffY);

  data.forEach((e, i) => {
    let curPoint, prePoint, nextPoint1, nextPoint2, x, y;

    // 当前点
    x = startX + diffX * i;
    y = baseY - (e - Min) * diffY;
    curPoint = new Point(x, y);

    // 前一个点
    x = startX + diffX * (i - 1);
    y = baseY - (data[i - 1] - Min) * diffY;
    prePoint = new Point(x, y);

    // 下一个点
    x = startX + diffX * (i + 1);
    y = baseY - (data[i + 1] - Min) * diffY;
    nextPoint1 = new Point(x, y);

    // 下下个点
    x = startX + diffX * (i + 2);
    y = baseY - (data[i + 2] - Min) * diffY;
    nextPoint2 = new Point(x, y);

    if (i === 0) {
      // 如果是第一个点, 则前一个点用当前点代替
      prePoint = curPoint;
    } else if (i === data.length - 2) {
      // 如果是倒数第二个点, 则下下个点用下一个点代替
      nextPoint2 = nextPoint1;
    } else if (i === data.length - 1) {
      // 最后一个点直接退出
      return;
    }

    const { controlPointA, controlPointB } = this.calcBezierControlPoints(
      prePoint,
      curPoint,
      nextPoint1,
      nextPoint2
    );

    ctx.bezierCurveTo(
      controlPointA.x,
      controlPointA.y,
      controlPointB.x,
      controlPointB.y,
      nextPoint1.x,
      nextPoint1.y
    );
  });

  ctx.stroke();
},
ログイン後にコピー

[関連する学習の推奨事項:

アプレット開発チュートリアル

]

以上がWeChat アプレットでキャンバスを使用して天気折れ線グラフを描画する方法を段階的に説明します (コード付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート