小程序—这款工具把加速计、陀螺仪、设备方向的调试痛点解决了

之前在一篇博文中捎带介绍过这款工具,反响很好,还收到了两位用户的打赏,但受那篇博文的影响,并没有被广大小程序开发者所熟知,故写一篇专门的,希望能有更多用户不再被加速计、陀螺仪、设备方向的调试难题再刺痛。

加速计、陀螺仪、设备方向痛点

1、小程序不支持开发工具调试,只支持真机调试
2、真机调试输出一堆日志,来不及做日志分析,就被顶上去

痛点解决方案

通过接收加速计、陀螺仪、设备方向返回的数据,实时绘制在折现图表上,这样一边晃动手机,一边可以观察图表中数据的变化,能够很快分辨出动作所带来的数据变化,方便调试。

实操效果

工具支持描述

加速计:X轴加速度、Y轴加速度、Z轴加速度的数据展示
陀螺仪:X轴角速度、Y轴角速度、Z轴角速度的数据展示
设备方向:α角、β角、γ角的数据展示
如下:

工具使用路径

工具系列>>硬件数据监听工具
小程序—这款工具把加速计、陀螺仪、设备方向的调试痛点解决了_第1张图片

单纯使用,无需代码

调用接口:

加速计:

	wx.onAccelerometerChange(function (res) {
      console.log("加速计数据变化", res)
    })

陀螺仪api:

	wx.startGyroscope({
      interval: 'normal',
      success: function (res) {
        console.log('陀螺仪监听成功', res)
      },
      fail: function (res) {
        console.log('陀螺仪监听失败', res)
      }
    })
    wx.onGyroscopeChange(function (res) {
      console.log('陀螺仪数据变化',res)
    })

设备方向api

	wx.startDeviceMotionListening({
      interval: 'normal',
      success: function (res) {
        console.log('设备方向监听成功', res)
      },
      fail: function (res) {
        console.log('设备方向监听失败', res)
      }
    })
    wx.onDeviceMotionChange(function (res) {
      console.log('设备方向数据变化',res)
    })

图表代码:

exports.init = function (ctx, options) {
    return new LineChart(ctx, options);
};

function LineChart(ctx, options) {
    this.ctx = wx.createCanvasContext(ctx);
    this.tipsCtx = options.tipsCtx ? wx.createCanvasContext(options.tipsCtx) : null;

    this.options = Object.assign({
        width: 320,
        height: 200,
        labelColor: '#888888',
        axisColor: '#d0d0d0',
        xUnit: '',
        yUnit: '',
        xAxis: [],
        lines: [],
        margin: 20,
        fontSize: 12,
    }, options, {
        xAxisOffset: (options.margin || 20) + (options.fontSize || 12) * 1.5
    });

    this._attrs = {};

    if (_.isEmpty(this.options.xAxis)) {
        throw new Error('options.xAxis can not be empty');
    }

    if (_.isEmpty(this.options.lines)) {
        throw new Error('options.lines can not be empty');
    }
}

LineChart.prototype.draw = function () {
    this._clear();

    this._drawAxis();
    this._drawLines();

    this._draw();
};

LineChart.prototype.hideLine = function (index) {
    let {
        lines
    } = this.options;

    if (lines[index]) {
        lines[index].hidden = true;
        this.draw();
    }
};

LineChart.prototype.showLine = function (index) {
    let {
        lines
    } = this.options;

    if (lines[index] && lines[index].hidden) {
        lines[index].hidden = false;
        this.draw();
    }
};

LineChart.prototype.tipsByX = function (x) {
    if (!this.options.tipsCtx) {
        return;
    }

    let {
        tipsIndex
    } = this._attrs;
    let index = this._indexByX(x);
    if (index === tipsIndex) {
        return;
    }
    this._attrs.tipsIndex = index;

    this._tipsLineByIndex(index);
    this._tipsLabelByIndex(index);

    this.tipsCtx.draw();
};

LineChart.prototype.clearTips = function () {
    if (!this.options.tipsCtx) {
        return;
    }

    let {
        width,
        height
    } = this.options;

    this.tipsCtx.clearRect(0, 0, width, height);
    this.tipsCtx.draw();
};

LineChart.prototype._tipsLabelByIndex = function (index) {
    let {
        xAxis,
        xUnit,
        yUnit,
        margin
    } = this.options;
    let {
        xOffset
    } = this._attrs;

    let ctx = this.tipsCtx;
    ctx.setFontSize(12);

    let title = xAxis[index] + xUnit;
    let points = this._pointsByIndex(index);
    points = _.map(points, item => ({
        color: item.color,
        text: `${item.value}${yUnit}`,
        width: ctx.measureText(`${item.value}${yUnit}`).width + 15,
        x: item.x
    }));

    let width = _.max(_.map(points, item => item.width));
    width = Math.max(ctx.measureText(title).width, width) + 20;
    let fromX = points[0].x - width;
    if (fromX < xOffset) {
        fromX = points[0].x;
    }
    let endX = fromX + width;
    let fromY = margin;
    let endY = margin + (points.length + 1) * 18;

    // 背景
    ctx.beginPath();
    ctx.moveTo(fromX, fromY);
    ctx.lineTo(fromX, endY);
    ctx.lineTo(endX, endY);
    ctx.lineTo(endX, fromY);
    ctx.closePath();
    ctx.globalAlpha = 0.4;
    ctx.fillStyle = 'black';
    ctx.fill();

    // 文字
    ctx.globalAlpha = 1;
    ctx.fillStyle = 'white';
    ctx.fillText(title, fromX + 10, fromY + 15);
    _.each(points, (item, index) => {
        let textY = fromY + 15 * (index + 2);

        ctx.beginPath();
        ctx.arc(fromX + 15, textY - 4, 5, 0, 2 * Math.PI);
        ctx.closePath();
        ctx.fillStyle = item.color;
        ctx.fill();
        ctx.fillStyle = 'white';
        ctx.fillText(item.text, fromX + 25, textY);
    });
};

LineChart.prototype._tipsLineByIndex = function (index) {
    let {
        yOffset
    } = this._attrs;
    let {
        margin,
        labelColor
    } = this.options;

    let points = this._pointsByIndex(index);
    let ctx = this.tipsCtx;

    // 实心点
    _.each(points, item => {
        ctx.beginPath();
        ctx.arc(item.x, item.y, 2, 0, 2 * Math.PI);
        ctx.closePath();
        ctx.fillStyle = item.color;
        ctx.fill();
    });

    ctx.beginPath();
    ctx.lineWidth = 0.3;
    ctx.strokeStyle = labelColor;
    ctx.moveTo(points[0].x, yOffset);
    ctx.lineTo(points[0].x, margin);
    ctx.stroke();
};

LineChart.prototype._indexByX = function (x) {
    let {
        xOffset,
        xStep
    } = this._attrs;
    let {
        xAxis
    } = this.options;

    let index = 0;
    if (x > xOffset) {
        index = Math.min(Math.round((x - xOffset) / xStep), xAxis.length - 1);
    }

    return index;
};

LineChart.prototype._pointsByIndex = function (index) {
    let {
        xOffset,
        xStep,
        yOffset,
        yStep
    } = this._attrs;

    return _.map(_.filter(this.options.lines, item => !item.hidden && !_.isUndefined(item.points[index])), item => ({
        x: xOffset + index * xStep,
        y: yOffset - item.points[index] * yStep,
        value: item.points[index],
        color: item.color
    }));
};

LineChart.prototype._clear = function () {
    let {
        width,
        height
    } = this.options;

    this.ctx.clearRect(0, 0, width, height);
};

LineChart.prototype._drawAxis = function () {
    let {
        width,
        height,
        lines,
        labelColor,
        axisColor,
        xUnit,
        yUnit,
        xAxis,
        margin,
        xAxisOffset,
        fontSize
    } = this.options;
    let ctx = this.ctx;

    ctx.setFontSize(fontSize);

    let yAxisLen = height - margin - xAxisOffset;
    let yLabelCount = Math.floor(yAxisLen / 25);
    let yMaxValue = _.max(_.map(lines, item => _.max(item.points))) || 1;

    // 计算需要绘制的y轴label
    let fixed = 0;
    let yDelta = yMaxValue * 1.2 / yLabelCount;
    if (yDelta < 1) {
        fixed = Math.round(1 / yDelta).toString().length;
    }
    yDelta = Number(fixed === 0 ? Math.ceil(yDelta) : yDelta.toFixed(fixed));
    let labels = [];
    for (let i = 2; i <= yLabelCount; i++) {
        labels.push(Number(((i - 1) * yDelta).toFixed(fixed)) + yUnit);
    }

    let xLabelMaxWidth = _.max(_.map(xAxis, item => ctx.measureText(item + xUnit).width));
    let yLabelMaxWidth = _.max(_.map(labels, item => ctx.measureText(item).width)) + margin;
    let xAxisLen = width - margin - yLabelMaxWidth;

    let xOffset = yLabelMaxWidth;
    let xStep = xAxisLen / (xAxis.length - 1);
    let yOffset = margin + yAxisLen;
    let yStep = yAxisLen / (yMaxValue * 1.2);

    // 绘制x轴
    ctx.beginPath();
    ctx.lineWidth = 1;
    ctx.strokeStyle = axisColor;
    ctx.moveTo(yLabelMaxWidth, height - xAxisOffset);
    ctx.lineTo(width - margin, height - xAxisOffset);
    ctx.stroke();

    // 绘制x轴label
    let xLabelCount = Math.floor(xAxisLen / xLabelMaxWidth);
    let xLabelStep = Math.ceil(xAxis.length / xLabelCount);
    // 需要被绘制的lable
    let xLabel = _.filter(_.map(xAxis, (item, index) => ({
        name: item + xUnit,
        index: index
    })), (item, index) => index % xLabelStep === 0);
    _.each(xLabel, item => {
        let xValue = xOffset + item.index * xStep - xLabelMaxWidth / 2 - 2;
        ctx.fillStyle = labelColor;
        ctx.fillText(item.name, xValue, height - margin);
    });

    // 绘制y轴label,以及水平标记线
    _.each(labels, (item, index) => {
        let xValue = (yLabelMaxWidth - ctx.measureText(item).width) - 5;
        let yValue = yOffset - yStep * Number((index + 1) * yDelta).toFixed(fixed);

        // 水平标记线
        ctx.beginPath();
        ctx.lineWidth = 0.8;
        ctx.strokeStyle = axisColor;
        ctx.moveTo(yLabelMaxWidth, yValue);
        ctx.lineTo(width - margin, yValue);
        ctx.stroke();

        // label
        ctx.strokeStyle = labelColor;
        ctx.fillText(item, xValue, yValue + 4);
    });

    // 将这几个数据存放在attrs上,绘制线的时候有用
    Object.assign(this._attrs, {
        xOffset,
        yOffset,
        xStep,
        yStep
    });
};

LineChart.prototype._drawLines = function () {
    _.each(this.options.lines, item => {
        if (item.hidden) {
            return;
        }

        this._drawLine(item);
    });
};

LineChart.prototype._drawLine = function (line) {
    let {
        xOffset,
        yOffset,
        xStep,
        yStep
    } = this._attrs;
    let ctx = this.ctx;

    let points = _.map(line.points, (item, index) => ({
        x: xOffset + index * xStep,
        y: yOffset - item * yStep
    }));

    // 与x轴的面积阴影
    ctx.beginPath();
    ctx.globalAlpha = 0.2;
    ctx.fillStyle = line.color;
    ctx.moveTo(xOffset, yOffset);
    _.each(points, item => {
        ctx.lineTo(item.x, item.y);
    });
    ctx.lineTo(xOffset + xStep * (points.length - 1), yOffset);
    ctx.closePath();
    ctx.fill();

    // 线
    ctx.beginPath();
    ctx.globalAlpha = 1;
    ctx.lineWidth = 1;
    ctx.strokeStyle = line.color;
    _.each(points, item => {
        ctx.lineTo(item.x, item.y);
    });
    ctx.stroke();

    // 空心点
    _.each(points, item => {
        ctx.beginPath();
        ctx.arc(item.x, item.y, 2, 0, 2 * Math.PI);
        ctx.closePath();
        ctx.fillStyle = 'white';
        ctx.fill();
        ctx.stroke();
    });
};

LineChart.prototype._draw = function () {
    if (this._timer) {
        clearTimeout(this._timer);
    }

    this._timer = setTimeout(() => {
        this.ctx.draw();
    });
};

你可能感兴趣的:(微信小程序,图表系列,工具,微信小程序,加速计,陀螺仪,设备方向,Accelerometer,Gyroscope,DeviceMotion)