更新记录

1.0.1(2025-03-03)

重要:新增图表点击事件显示图标详情消息框 重要:新增二维码和条形码生成

1.0.0(2024-12-19)

wui-charts 1.0.0


平台兼容性

Vue2 Vue3
×
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
Android:4.4,iOS:9,HarmonyNext:不确定 × × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序 鸿蒙元服务
× × × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× × × × × × ×

wui-charts开发文档介绍

简介:

wui-charts 它不是通过 Webview 套壳来实现图表功能,而是纯UTS编写 Canvas 绘制的轻量级图表库,
弥补了 uniapp-x 在app端渲染画布图表追求性能的需求,同时也填补了uniapp-x的插件生态
目前包含了大多数常用的图表,后期会陆续增加。特此声明:不兼容旧版uniapp他是uniapp-x的api

优势:

纯 UTS 编写:wui-charts 完全采用 UTS 编程语言,保证了图表的执行效率和性能。
Canvas 绘制:直接在 Canvas 上绘制图表,避免了 Webview 的性能开销,使得图表更加流畅。
轻量级:无需依赖额外的 Webview,减少了应用的包体积,提升了加载速度。

新增点击事件显示详情消息框

<view style="background-color: aqua;align-items: center;padding:2px;">
    <canvas canvas-id="canvas" id="canvas" @click="setData"></canvas>
</view>
export default {
    data() {
        return {
            // 处理双击事件
            clickTimes: 0,
            //实例化插件传入canvas的id
            charts: null as wuiCharts | null,
            //初始化前准备的配置项
            opts: {
                //图表类型
                type: 'columnar1',
                //初始化canvas的宽请与css设置一直rpx需要转成px
                width: 350,
                //初始化canvas的高请与css设置一直rpx需要转成px
                height: 250,
                //初始化canvas的背景色
                bgColor: 'rgba(255,255,255,1)',
                //初始化内边距,绘制时按照内边距向内绘制
                padding: 10,
                //初始竖轴刻度和参照标识
                reference: ["0", "200", "400", "600", "800", "1000"],
                //初始横轴刻度和参照标识和数据长度一直
                series: ['2021', '2022', '2023', '2024', '2025', '2026', '2027'],
                //动画过度时间animate为true时有效
                speed: 50,
                //是否开启过度动画,设定固定数据时的过度动画
                animate: true,
                //总刻度值,数据参照的最大值
                total: 1000,
                data: [
                    { "value": 300, color: '#cbcbcb', bgColor: 'green' },
                    { "value": 900, color: '#cbcbcb', bgColor: 'green' },
                    { "value": 500, color: '#cbcbcb', bgColor: 'green' },
                    { "value": 800, color: '#cbcbcb', bgColor: 'green' },
                    { "value": 600, color: '#cbcbcb', bgColor: 'green' },
                    { "value": 400, color: '#cbcbcb', bgColor: 'green' },
                    { "value": 300, color: '#cbcbcb', bgColor: 'green' },
                ] as UTSJSONObject[]
            } as UTSJSONObject,
            //数据和横轴series长度一直
            data: [
                { "value": 300, color: '#cbcbcb', bgColor: 'green' },
                { "value": 900, color: '#cbcbcb', bgColor: 'green' },
                { "value": 500, color: '#cbcbcb', bgColor: 'green' },
                { "value": 800, color: '#cbcbcb', bgColor: 'green' },
                { "value": 600, color: '#cbcbcb', bgColor: 'green' },
                { "value": 400, color: '#cbcbcb', bgColor: 'green' },
                { "value": 300, color: '#cbcbcb', bgColor: 'green' },
            ] as UTSJSONObject[]
        }
    },
    onReady() {
        //初始化画布
        this.charts = new wuiCharts('canvas', this.opts);
    },
    methods: {

        /* 双击更新数据 单击显示详情消息框*/
        setData(e : UniPointerEvent) {
            this.clickTimes++;
            if (this.clickTimes == 2) {
                this.clickTimes = 0;
                //  处理双击事件...
                this.charts!.setSpeed(50);
                this.charts!.setAnimate(true);
                this.charts!.drawing(this.data);
            }
            setTimeout(() => {
                if (this.clickTimes == 1) {
                    this.clickTimes = 0;
                    //  处理单击事件...
                    this.charts!.menuEvent(e.clientX, e.clientY);
                }
            }, 250)
        },

    }
}

新增二维码和条形码生成

<view style="background-color: #dfdfdf;align-items: center;padding:2px;">
    <canvas canvas-id="canvas1" id="canvas1" @click="setData1"></canvas>
    <view class="btn" style="margin-left: 5px;" @click="btnClick('QRCODE')">
        <text class="btnbox-txt">点击生成</text>
    </view>
</view>
<view style="background-color: #dfdfdf;align-items: center;padding:2px;">
    <canvas canvas-id="canvas2" id="canvas1" @click="setData1"></canvas>
    <view class="btn" style="margin-left: 5px;" @click="btnClick('CODE128')">
        <text class="btnbox-txt">点击生成</text>
    </view>
</view>
<view style="background-color: #dfdfdf;align-items: center;padding:2px;">
    <canvas canvas-id="canvas3" id="canvas1" @click="setData1"></canvas>
    <view class="btn" style="margin-left: 5px;" @click="btnClick('CODEEAN13')">
        <text class="btnbox-txt">点击生成</text>
    </view>
</view>

export default {
    data() {
        return {
            clickTimes: 0,
            //实例化插件传入canvas的id
            QRCODE: null as wuiCharts | null,
            CODE128: null as wuiCharts | null,
            CODEEAN13: null as wuiCharts | null,
            focus: false,
            err: '',
            logoname: '',
            //数据和横轴series长度一直
            opts1: {
                type: 'QRCODE',
                size: 150,
                logoSrc: '',
                // iconSize: 25,
                padding: 10,
                fgColor: '#000000',//前景色
                bgColor: '#ffffff',//画布背景色
                QRErr: 'M',// 设置错误修正级别为
                isSRT: true,
                speed: 30,
                animate: true,
                data:''
            } as UTSJSONObject,
            opts2: {
                type: 'CODE128',
                // text:'扫一扫',
                // textMwidth:20,
                speed: 30,
                animate: true,
                maxwidth: 350,
                maxheight: 190,
                height: 80,
                data: ''
            } as UTSJSONObject,
            opts3: {
                type: 'CODEEAN13',
                height: 80,
                speed: 30,
                animate: true,
                data: ''
            } as UTSJSONObject,
            data1: '更改成你自己的文本内容',
            data2: 'CODE-128-0',
            data3: '690456789088',
        }
    },
    onReady() {
        this.setopts();
        this.opts1.data = this.data1;
        this.opts2.data = this.data2;
        this.opts3.data = this.data3;
        this.QRCODE = new wuiCharts('canvas1', this.opts1);
        this.CODE128 = new wuiCharts('canvas2', this.opts2)
        this.CODEEAN13 = new wuiCharts('canvas3', this.opts3)
    },
    methods: {

        btnClick(type : string) {
            if (type == 'QRCODE') {
                this.QRCODE!.setLogo(`${this.opts1.logoSrc}`);
                this.QRCODE!.setSpeed(30);
                this.QRCODE!.setAnimate(true);
                this.QRCODE?.drawing(this.data1)
            }
            if (type == 'CODE128') {
                this.CODE128!.setSpeed(30);
                this.CODE128!.setAnimate(true);
                this.CODE128?.drawing(this.data2)
            }
            if (type == 'CODEEAN13') {
                this.CODEEAN13!.setSpeed(30);
                this.CODEEAN13!.setAnimate(true);
                this.CODEEAN13?.drawing(this.data3);

            }
            if (type == '更改容错率') {
                uni.showActionSheet({
                    title: '注意:低容错率尽量不被遮挡才能保证识别率',
                    itemList: ['L 级大约 7% ', 'M 级大约 15%', 'Q 级大约 25%', 'H 级大约 35%'],
                    success: (res : ShowActionSheetSuccess) => {
                        if (res.tapIndex == 0) {
                            this.QRCODE?.setQRErr('L');
                            this.opts1.QRErr = 'L';
                        }
                        if (res.tapIndex == 1) {
                            this.QRCODE?.setQRErr('M');
                            this.opts1.QRErr = 'M';
                        }
                        if (res.tapIndex == 2) {
                            this.QRCODE?.setQRErr('Q');
                            this.opts1.QRErr = 'Q';
                        }
                        if (res.tapIndex == 3) {
                            this.QRCODE?.setQRErr('H');
                            this.opts1.QRErr = 'H';
                        }
                        this.QRCODE!.setLogo(`${this.opts1.logoSrc}`);
                        this.QRCODE!.setSpeed(30);
                        this.QRCODE!.setAnimate(true);
                        this.setopts();
                        setTimeout(() => {
                            this.QRCODE?.drawing(this.data1);
                        }, 200)
                    }
                })
            }
            if (type == '添加网络logo') {
                uni.showModal({
                    title: '网址以http或https或ftp开头的',
                    editable: true,
                    placeholderText: '请输入网络地址',
                    success: (res : ShowModalSuccess) => {
                        if (res.confirm) {
                            let testURL = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i;
                            let content = res.content!;
                            if (testURL.test(content)) {
                                this.opts1.logoSrc = content;
                                this.QRCODE!.setLogo(`${this.opts1.logoSrc}`);
                                this.QRCODE!.setSpeed(30);
                                this.QRCODE!.setAnimate(true);
                                this.setopts();
                                this.QRCODE?.drawing(this.data1)
                                console.log('合法网址');
                            } else {
                                uni.showToast({
                                    title: '网址无效',
                                    icon: 'none',
                                })
                                console.log('不合法网址');
                            }
                        }

                    }
                })
            }
            if (type == '添加本地logo') {
                uni.chooseImage({
                    albumMode: 'custom',
                    count: 1,
                    crop: {
                        width: 300,
                        height: 300,
                    } as ChooseImageCropOptions,
                    success: (callback : ChooseImageSuccess) => {
                        this.opts1.logoSrc = callback.tempFiles[0].path;
                        this.QRCODE!.setLogo(`${this.opts1.logoSrc}`);
                        this.QRCODE!.setSpeed(30);
                        this.QRCODE!.setAnimate(true);
                        this.setopts();
                        setTimeout(() => {
                            this.QRCODE?.drawing(this.data1);
                        }, 200)
                    }
                })
            }
        },

        setScale(type : string) {
            if (type == 'QRCODE') {
                let size = this.opts1.size as number;
                if (size == 150) {
                    this.opts1.size = 150 * 0.8;
                    this.QRCODE?.setScale(0.8);
                } else {
                    this.opts1.size = 150 * 1;
                    this.QRCODE?.setScale(1.25);
                }
                this.QRCODE!.setSpeed(30);
                this.QRCODE!.setAnimate(true);
                this.QRCODE?.drawing(this.data1)
            }
            if (type == 'CODE128') {
                let size = this.opts2.height as number;
                if (size == 80) {
                    this.opts2.height = 80 * 0.8;
                    this.CODE128?.setScale(0.8);
                } else {
                    this.opts2.height = 80 * 1;
                    this.CODE128?.setScale(1.25);
                }
                this.CODE128!.setSpeed(30);
                this.CODE128!.setAnimate(true);
                this.CODE128?.drawing(this.data2)
            }
            if (type == 'CODEEAN13') {
                let size = this.opts3.height as number;
                if (size == 80) {
                    this.opts3.height = 80 * 0.8;
                    this.CODEEAN13?.setScale(0.8);
                } else {
                    this.opts3.height = 80 * 1;
                    this.CODEEAN13?.setScale(1.25);
                }
                this.CODEEAN13!.setSpeed(30);
                this.CODEEAN13!.setAnimate(true);
                this.CODEEAN13?.drawing(this.data3)
            }

        },
        /* 更新canvas数据 */
        setData1(e : UniPointerEvent) {
            this.clickTimes++;
            if (this.clickTimes == 2) {
                this.clickTimes = 0;
                //  处理双击事件...
                this.QRCODE!.setLogo(`${this.opts1.logoSrc}`);
                this.QRCODE!.setSpeed(30);
                this.QRCODE!.setAnimate(true);
                this.QRCODE?.drawing(this.data1)
            }
            setTimeout(() => {
                if (this.clickTimes == 1) {
                    this.clickTimes = 0;
                    //  处理单击事件...
                    this.QRCODE!.menuEvent(e.clientX, e.clientY);
                }
            }, 250)
        },
        setData2(e : UniPointerEvent) {
            this.clickTimes++;
            if (this.clickTimes == 2) {
                this.clickTimes = 0;
                //  处理双击事件...
                this.CODE128!.setSpeed(30);
                this.CODE128!.setAnimate(true);
                this.CODE128?.drawing(this.data2)
            }
            setTimeout(() => {
                if (this.clickTimes == 1) {
                    this.clickTimes = 0;
                    //  处理单击事件...
                    this.CODE128!.menuEvent(e.clientX, e.clientY);
                }
            }, 250)
        },
        setData3(e : UniPointerEvent) {
            this.clickTimes++;
            if (this.clickTimes == 2) {
                this.clickTimes = 0;
                //  处理双击事件...
                this.CODEEAN13!.setSpeed(30);
                this.CODEEAN13!.setAnimate(true);
                this.CODEEAN13?.drawing(this.data3)
            }
            setTimeout(() => {
                if (this.clickTimes == 1) {
                    this.clickTimes = 0;
                    //  处理单击事件...
                    this.CODEEAN13!.menuEvent(e.clientX, e.clientY);
                }
            }, 250)
        },
        /* 保存 canvas到相册*/
        save(type : string) {
            let url : string | null = null;
            if (type == 'QRCODE') {
                url = this.QRCODE!.toDataURL();
            }
            if (type == 'CODE128') {
                url = this.CODE128!.toDataURL();
            }
            if (type == 'CODEEAN13') {
                url = this.CODEEAN13!.toDataURL();
            }
            if (url != null) {
                //保存图片
                const daturl = url.split(',');
                const dat = daturl[1] as any;
                uni.showActionSheet({
                    itemList: ['保存到相册'],
                    success: (e) => {
                        e.tapIndex;
                        if (e.tapIndex == 0) {
                            this.savimg(type, dat)
                        }

                    }
                })
            }
        },
        savimg(str : string, dat : any) {
            const fs = uni.getFileSystemManager();
            fs.writeFile({
                filePath: `${uni.env.CACHE_PATH}/${str}.png`,
                encoding: "base64",
                data: dat,
                success: (res : FileManagerSuccessResult) => {
                    console.log('写入成功', res);
                    uni.saveImageToPhotosAlbum({
                        filePath: `${uni.env.CACHE_PATH}/${str}.png`,
                        success: (callback : SaveImageToPhotosAlbumSuccess) => {
                            console.log('保存成功');
                            //保存成功后删除临时文件
                            fs.unlink({
                                filePath: `${uni.env.CACHE_PATH}/${str}.png`,
                                success: () => {
                                    console.log('删除成功');
                                },
                                fail: () => {
                                    console.log('删除失败');
                                }
                            } as UnLinkOptions)
                            uni.showToast({
                                title: "保存成功"
                            })
                        }
                    })
                }
            } as WriteFileOptions);
        }
    }
}

使用说明:

通过插件市场下载会自动导入插件到项目中之后只需简单三部。

1.在使用的页需面引入插件。
import { wuiCharts } from '@/uni_modules/wui-charts';
3.实例化wui-charts插件并绑定Canvas的id 以vue的选项api为例。
<template>
    <scroll-view style="flex:1" bounces="false">
        <view style="background-color: aqua;align-items: center;padding:2px;">
           <canvas canvas-id="canvas" id="canvas" style="width: 730rpx;height: 600rpx;" @click="setData"></canvas>
        </view>
    </scroll-view>
</template>

<script>
     // 引入wui-charts插件 
    import { wuiCharts } from '@/uni_modules/wui-charts';
    data() {
        return {
            //实例化插件传入canvas的id
            charts: new wuiCharts('canvas'),
            opts: {
                //图表类型
                type: 'columnar1',
                //初始化canvas的宽请与css设置一直rpx需要转成px
                width: uni.rpx2px(730),
                //初始化canvas的高请与css设置一直rpx需要转成px
                height: uni.rpx2px(600),
                //初始化canvas的背景色
                bgcolor: 'rgba(255,255,255,1)',
                //初始化内边距,绘制时按照内边距向内绘制
                padding: 20,
                //初始竖轴刻度和参照标识
                reference: ["0", "200", "400", "600", "800", "1000"],
                //初始横轴刻度和参照标识和数据长度一直
                series: ['2021', '2022', '2023', '2024', '2025', '2026'],
                //动画过度时间animate为true时有效
                speed: 50,
                //是否开启过度动画,设定固定数据时的过度动画
                animate: true,
                //总刻度值,数据参照的最大值
                total: 1000,
            } as UTSJSONObject,
            //数据和横轴series长度一直
            data: [
                {"value": 300},
                { "value": 900 },
                { "value": 500 },
                { "value": 800 },
                { "value": 600 },
                { "value": 400 },
            ] as UTSJSONObject[]
        }
    }
</script>
4.初始化画布并传入配置项 opts 数据类型为UTSJSONObject。
<script>
    onReady(){
        //初始化画布
        this.charts.init(this.opts);
    }
</script>
5.传入数据或者更新数据 data 此处数据类型为UTSJSONObject[]后面对每个图表的数据类型有详细介绍。
<script>
    onReady(){
        //初始化画布
        this.charts.init(this.opts);
        //传入数据或者更新数据
        this.charts.setData(this.data);
    }
</script>

此时图表在生命周期onReady之后就渲染到画布了。

vue组合API 简单实用实例

vue组合API也是在生命周期onReady之后初始化画布和更新数据。

<template>
    <scroll-view style="flex:1" bounces="false">
        <view style="background-color: aqua;align-items: center;padding:2px; height: 600rpx;justify-content: center;">
            <view style="width: 110px; border-radius: 110px;">
                <canvas canvas-id="canvas" id="canvas" style="width: 110px;height: 110px;" @click="setData"></canvas>
            </view>
        </view>
        <view class="slid" style="margin-top: 15px;">
            <slider style="flex: 1;" valueColor="#fff" @changing="changing" :value="data" :min="0" :max="100" :step="1"
                :show-value="true" />
        </view>
    </scroll-view>
</template>

<script setup  lang="uts">
    /* 引入wui-charts插件 */
    import { wuiCharts } from '@/uni_modules/wui-charts';
    //实例化并绑定canvas的id
    let charts = new wuiCharts('canvas');
    //画布配置项
    let opts = {
        type: 'annular',
        width: 110,
        height: 110,
        lineWidth: 10,
        bgcolor: 'rgba(255,255,255,1)',
        padding: 20,
        speed: 100,
        animate: true,
    } as UTSJSONObject;
    //画布数据
    let data = 50;
    let clickTimes = 0;
    //在生命周期onReady之后初始化和更新数据。
    onReady(() => {
      charts.init(opts);
      charts.setData(data);
    })  
    let setData = () => {
        clickTimes++;
        if (clickTimes == 2) {
            clickTimes = 0;
            //  处理双击事件...
            opts['animate'] = true;
            charts.setData(data);
        }
        setTimeout(function () {
            if (clickTimes == 1) {
                clickTimes = 0;
                //  处理单击事件...           
            }
        }, 250)
    };
    let changing = (e : UniSliderChangeEvent) => {
        opts['animate'] = false;
        data = e.detail.value;
        charts.setData(e.detail.value);
    };
</script>
<style>
    .slid {
        display: flex;
        justify-content: space-around;
        flex-direction: row;
        align-items: center;
        height: 40px;
        margin: 2px 10px;
        background-color: #00aaff;
        border: 1rpx #999 solid;
        border-radius: 4px;
        padding-left: 20px;
    }
</style>

wui-charts目前暴露的有两个方法 init初始化方法 和 setData更新数据方法。

其余的绘制方法都是模块化的只需在初始化 init 方法传入的opts配置项中配置即可。

init参数opts类型UTSJSONObject 通用。
除了type是必须配置其他可忽略。
注意:宽和高要和Canvas的css样式设置一直,rpx需要在配置宽和高的时候uni.rpx2px做转换,否则显示大小不一致
    ```
    opts:{
        //图表类型
        type: 'columnar1',
        //初始化canvas的宽请与css设置一直rpx需要转成px
        width: uni.rpx2px(730),
        //初始化canvas的高请与css设置一直rpx需要转成px
        height: uni.rpx2px(600),
        //初始化canvas的背景色
        bgcolor: 'rgba(255,255,255,1)',
        //初始化内边距,绘制时按照内边距向内绘制
        padding: 20,
        //初始竖轴刻度和参照标识
        reference: ["0", "200", "400", "600", "800", "1000"],
        //初始横轴刻度和参照标识和数据长度一直
        series: ['2021', '2022', '2023', '2024', '2025', '2026'],
        //动画过度时间animate为true时有效
        speed: 50,
        //是否开启过度动画,设定固定数据时的过度动画
        animate: true,
        //总刻度值,数据参照的最大值
        total: 1000,
    } as UTSJSONObject
    ````
setData参数data类型any根据不同图表传入不同类型数据。

每个图表种类的data数据说明

columnar1 单柱状图
    //长度要和 opts的series 一直
    data:[{
        "value": 300, //数据值
        "color": "pink",//文字颜色
        "bgcolor": "blue"//柱状颜色
    }] as UTSJSONObject[]

columnar2 重叠双柱状图
    //长度要和 opts的series 一直
    data:[{
        "value": 300, //完成数据值
        "target": 400, //目标数据值
    }] as UTSJSONObject[]

columnar3 正负单柱状图
    //长度要和 opts的series 一直
    data:[{
        "value": -700,//正负数据值
        "color": "pink",//文字颜色
        "bgcolor": "blue"//柱状颜色
    }] as UTSJSONObject[]

line1 单折线图
    //长度要和 opts的series 一直
    data:[{
        "value": 700,//数据值
        "color": "#000"//每条线段的颜色,不设置默认黑色
    }] as UTSJSONObject[]

line2 多折线图
    //长度要和 opts的series 一直
    data: [{            
        "name": '酸枣仁',
        "color": 'red',//每项颜色,不设置默认黑色
        "data":[{
            "value":200,//数据值
            "color":"#000",//每条线段的颜色,不设置默认黑色 
        }]
    }] as UTSJSONObject[]

pieshaped 饼状图
    //长度无要求,适度即可
    data:[{
        "value": 30,//数据值
        "color": "black",//每一项的颜色
        "title": "一班"//每一项的标题名称
    }] as UTSJSONObject[]

radar 雷达图
    //长度无要求,适度即可
    data:[{
        "value": 70, //数据值
        "title": "超大盘",//每一项的标题名称
    }] as UTSJSONObject[]

dashboard 仪表盘
    //需要在初始化opts的时候配置 total 总刻度,如不配置默认是100
    //数据值number类型
    data:80,

annular 环形进度条
    //数据值number类型
    data:80,

ball 水球波纹进度
    //数据值number类型
    data:80,

下载app体验实例

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问