uniapp使用wxml-to-canvas开发小程序保存canvas图片

06-01 1115阅读

微信小程序官方解决方案:wxml-to-canvas

使用wxml-to-canvas要知道一些前提条件
1、只能画view,text,image
2、每个元素必须要设置宽高
3、默认是flex布局,可以通过flexDirection: "column"来改变排列方式
4、文字 必须放在text中,放在view中无法显示,且text不支持字体加粗
5、如果你要设置背景颜色,请使用backgroundColor,而非background,border同理~
6、多个absolute元素时,因为没有z-index,template元素自上而下渲染,对应z-index依次增高
7、导出图片过大,可以通过canvasToTempFilePath({fileType, quality})配置里面的quality字段来减小
8、image只支持临时地址和网络地址,不支持base64和本地图片,可以通过writeFile 把base64转成临时地址
如下:
const fs = wx.getFileSystemManager();
let qrcodeBase64 = QRresult.data;
let qrcodeLink = `${wx.env.USER_DATA_PATH}/qrcodeLink.gif`;
fs.writeFile({
    filePath: qrcodeLink,
    data: qrcodeBase64,
    encoding: 'base64',
    success: res => {
        console.log(res)
        shopJson.qrcode = qrcodeLink;
        this.renderToCanvas();
    },
    fail(res) {
        console.error(res)
    }
})
9、Canvas 初始化问题:为了正确绘制图像,需要确保在调用 canvas.draw() 之前完成所有绘制操作。
10、不支持实时更新:一旦 Canvas 绘制完成,它将成为静态图像,无法实时更新。如果需要实时更新数据可视化图表,可以考虑
使用其他库或组件。
11、长按保存图片问题:wxml-to-canvas 组件默认无法通过长按保存图片功能保存 Canvas 图像。你可能需要自行实现该功能,
并适配不同平台的实现方式。
12、对低版本小程序的兼容性问题:某些较旧的微信小程序版本可能无法完全支持 wxml-to-canvas 组件。在开发之前,请确保目
标用户群体的微信小程序版本。
13、性能问题:复杂的数据可视化图表可能会影响性能,尤其是在绘制大量数据时。尽量优化绘制逻辑,以避免出现卡顿或延迟。
14、与其他组件的兼容性问题:wxml-to-canvas 组件可能与其他小程序组件存在一些兼容性问题,导致样式错乱或布局问题。建
议在使用时进行充分的测试和调试。
15、跨平台问题:wxml-to-canvas 组件目前主要针对微信小程序,可能无法直接适用于其他小程序平台或移动端框架。如果需要
在其他平台上实现类似的功能,可能需要另行寻找适合的解决方案。
16、Canvas 绘图能力限制:由于 Canvas 的绘图能力有限,某些高级的数据可视化需求可能无法直接通过 wxml-to-canvas 组
件实现。在确定方案之前,建议先了解 Canvas 绘图的限制和特性。
17、样式定制问题:某些样式属性或效果可能难以通过 wxml-to-canvas 组件实现,例如阴影、渐变色等。需要根据具体需求考虑
是否能够满足所需的样式效果。
18、开发者工具与真机表现差异:在进行调试和预览时,开发者工具上的表现可能与真机上存在一些差异。建议进行真机测试,以
确保数据可视化图表在不同设备上正常显示。
等等...其他未知问题...

一、安装

npm install --save wxml-to-canvas

二、在程序根目录下新建 wxcomponents 文件夹,将node_modules下的 widget-ui 和 wxml-to-canvas 两个文件夹复制进去。

uniapp使用wxml-to-canvas开发小程序保存canvas图片

注意:这里有的安装后生成的node_modules还不一样,可直接复制dist里面的即可(如下图)

uniapp使用wxml-to-canvas开发小程序保存canvas图片

 三、将/wxcomponents/wxml-to-canvas/index.js中的

module.exports = require("widget-ui");
//改为
module.exports = require("../widget-ui/index.js")

 四、配置pages.json (这样uni-app才会打包wxcomponents)

  • 1. 在需要用到的页面配置
    "pages": [
        {
            "path": "pages/xxx",
            "style": {
                "usingComponents": {
                  "wxml-to-canvas": "/wxcomponents/wxml-to-canvas/index"
                }
            }
        },
    ...
    ]
    • 2. 或者在globalStyle里面全局配置
      "globalStyle": {
      		"navigationBarTextStyle": "black",
      		"navigationBarTitleText": "uni-app",
      		"navigationBarBackgroundColor": "#F8F8F8",
      		"backgroundColor": "#F8F8F8",
      		"usingComponents": {
      			"wxml-to-canvas": "/wxcomponents/wxml-to-canvas/index"
      		}
      },

      五、重点来了,如何使用?

      •  页面配置
        
        
          
            
            
              
                
              
              
                {{ msg }}
              
              
                保存图片
              
            
          
        
        
        import { wxml, style } from './DomData';
        export default {
          data() {
            return {
              show: false, // 是否显示canvas
              canvasWidth: 320, // 默认canvas宽高
              canvasHeight: 480,
              screenWidth: null, // 设备宽度
              screenHeight: null, // 设备高度
              // name: '',
              // pic: '',
              // chapter1: '',
              // chapter2: '',
              widget: null,
              msg: '加载中,请稍等...', // 提示语
              listData:[
                {
                  type:'1',
                  title:'标题一',
                  auther: '张三  2024-01-10',
                  imgsrc:'https://pic1.zhimg.com/80/v2-58fe538a59f870407b1435bfd45893ed_720w.jpeg',
                  text:'本报讯(记者 杜晨薇)上海正在建设东方枢纽项目,浦东国际机场与沪通铁路将实现连通,枢纽周边临空产业、重点项目布局正在规划设计中。为落实上海市政府与中国东方航空集团有限公司(以下简称“中国东航”)合作协议要求,充分利用东方枢纽建设发展机遇期进一步深化合作发展,昨天下午,浦东新区政府与中国东航签署深化合作战略协议。'
                },
                {
                  type: '2',
                  title: '标题二',
                  auther: '张三  2024-01-10',
                  imgsrc: 'https://pic1.zhimg.com/80/v2-58fe538a59f870407b1435bfd45893ed_720w.jpeg',
                  text: '本报讯(记者 杜晨薇)上海正在建设东方枢纽项目,浦东国际机场与沪通铁路将实现连通,枢纽周边临空产业、重点项目布局正在规划设计中。为落实上海市政府与中国东方航空集团有限公司(以下简称“中国东航”)合作协议要求,充分利用东方枢纽建设发展机遇期进一步深化合作发展,昨天下午,浦东新区政府与中国东航签署深化合作战略协议。'
                },
                {
                  type: '3',
                  title: '标题三',
                  auther: '张三  2024-01-10',
                  imgsrc: 'https://pic1.zhimg.com/80/v2-58fe538a59f870407b1435bfd45893ed_720w.jpeg',
                  text: '本报讯(记者 杜晨薇)上海正在建设东方枢纽项目,浦东国际机场与沪通铁路将实现连通,枢纽周边临空产业、重点项目布局正在规划设计中。为落实上海市政府与中国东方航空集团有限公司(以下简称“中国东航”)合作协议要求,充分利用东方枢纽建设发展机遇期进一步深化合作发展,昨天下午,浦东新区政府与中国东航签署深化合作战略协议。'
                },
                {
                  type: '4',
                  title: '标题四',
                  auther: '张三  2024-01-10',
                  imgsrc: 'https://pic1.zhimg.com/80/v2-58fe538a59f870407b1435bfd45893ed_720w.jpeg',
                  text: '本报讯(记者 杜晨薇)上海正在建设东方枢纽项目,浦东国际机场与沪通铁路将实现连通,枢纽周边临空产业、重点项目布局正在规划设计中。为落实上海市政府与中国东方航空集团有限公司(以下简称“中国东航”)合作协议要求,充分利用东方枢纽建设发展机遇期进一步深化合作发展,昨天下午,浦东新区政府与中国东航签署深化合作战略协议。'
                },
                {
                  type: '5',
                  title: '标题五',
                  auther: '张三  2024-01-10',
                  imgsrc: 'https://pic1.zhimg.com/80/v2-58fe538a59f870407b1435bfd45893ed_720w.jpeg',
                  text: '本报讯(记者 杜晨薇)上海正在建设东方枢纽项目,浦东国际机场与沪通铁路将实现连通,枢纽周边临空产业、重点项目布局正在规划设计中。为落实上海市政府与中国东方航空集团有限公司(以下简称“中国东航”)合作协议要求,充分利用东方枢纽建设发展机遇期进一步深化合作发展,昨天下午,浦东新区政府与中国东航签署深化合作战略协议。'
                },
              ]
            }
          },
          methods: {
            // wxml 转 canvas
            renderToCanvas() {
              console.log('canvasStyle.widget', this.widget)
              const _wxml = wxml(this.listData);
              console.log('this.widget', _wxml)
              const _style = style(this.screenWidth, this.canvasWidth, this.canvasHeight) //this.canvasHeight
              const p1 = this.widget.renderToCanvas({ wxml: _wxml, style: _style })
              console.log('renderToCanvas', p1)
              p1.then((res) => {
                console.log('海报生成成功', res);
                wx.hideLoading()
              }).catch((err) => {
                console.log('生成失败', err)
              })
            },
            // 保存到朋友圈
            extraImage() {
              if (!this.show) {
                wx.showToast({ title: '海报生成失败,无法分享到朋友圈', icon: 'none' })
                return
              }
              wx.showLoading({ title: '海报生成中...' })
              const p2 = this.widget.canvasToTempFilePath({ fileType:'jpg', quality :0.5})
              let that = this;
              p2.then(result => {
                let path = result.tempFilePath
                wx.getSetting({
                  success: res => {
                    wx.hideLoading()
                    // 非初始化且未授权的情况,需要再次弹窗提示授权
                    if (res.authSetting['scope.writePhotosAlbum'] != undefined && res.authSetting['scope.writePhotosAlbum'] != true) {
                      wx.showModal({
                        title: '是否授权相册权限',
                        content: '需要获取相册权限,请确认授权,否则无法使用相关功能',
                        success: res => {
                          if (res.confirm) {
                            wx.openSetting({
                              success: dataAu => {
                                if (dataAu.authSetting["scope.writePhotosAlbum"] == true) {
                                  wx.showToast({
                                    title: '授权成功',
                                    icon: 'none',
                                    duration: 1000
                                  });
                                  that.saveIMg(path);
                                } else {
                                  wx.showToast({
                                    title: '授权失败',
                                    icon: 'success',
                                    duration: 1000
                                  });
                                }
                              }
                            });
                          }
                        }
                      });
                    } else {
                      // 初始化且未授权,系统默认会弹窗提示授权
                      // 非初始化且已授权,也会进入这里
                      that.saveIMg(path);
                    }
                  }
                });
              })
            },
            // 保存到相册
            async saveIMg(tempFilePath) {
              wx.saveImageToPhotosAlbum({
                filePath: tempFilePath,
                success: async (res) => {
                  wx.showModal({
                    content: '图片已保存,分享给好友吧!',
                    showCancel: false,
                    confirmText: '好的',
                    confirmColor: '#333',
                    success: function (res) {
                      wx.navigateBack({
                        //返回
                        delta: 1
                      });
                    },
                    fail: function (res) {
                      console.log('res', res);
                    }
                  });
                },
                fail: function (res) {
                  wx.showToast({
                    title: '您取消了授权',
                    icon: 'none',
                    duration: 2000
                  })
                }
              });
            },
          },
          onLoad(options) {
            console.log('options', options);
            // 获取设备信息
            wx.getSystemInfo({
              success: (res) => {
                console.log('屏幕',res)
                this.screenWidth = res.screenWidth;
                this.canvasWidth = this.screenWidth;
                this.canvasHeight = this.screenWidth * 8.5;
                console.log('海报高度:', this.canvasHeight)
                this.show = true
                // 数字容器宽度 动态设置 
                setTimeout(() => {
                  wx.showLoading({ title: '海报加载中...' })
                  this.widget = this.selectComponent('.widget')
                  this.renderToCanvas()
                }, 1000)
              }
            });
          },
        }
        
        
        .share-page {
          background: #cc0202;
          position: relative;
          overflow: hidden;
          // padding: 10rpx;
        //   min-height: 100vh;
          .msg-box {
            display: flex;
            align-items: center;
            text-align: center;
            justify-content: center;
          }
          .share-page-box {
            margin: 0 auto;
            position: relative;
            overflow: hidden;
        	  box-shadow: 0rpx 6rpx 20rpx 6rpx rgba(0, 0, 0, 0.2);
          }
          .share-page-btn {
            margin: 0 10rpx 0 10rpx;
            img {
              width: 100%;
              height: 100%;
            }
          }
        }
        
        
        • DomData.js

          /**
           *
           * @param {*} listData  canvas数据
           */
          export const wxml = (listData) => `
          
            ${listData.map(item=>{
              return `
              
                
                  
                  `+ item.title + `
                  `+ item.auther + `
                
                
                  
                  
                    `+ item.text+`
                  
                
              `
            }).join('')}
          
          `
          /**
           *
           *
           * @param {*} screenWidth 屏幕宽度
           * @param {*} canvasWidth  画布宽度
           * @param {*} canvasHeight  画布高度
           * @param {*} numberWidth  数字宽度,动态设置
           * @return {*} 
           */
           export const style = (screenWidth, canvasWidth, canvasHeight) => {
            return {
              "container": {
                width: canvasWidth,
                minHeight: canvasHeight,
                position:'relative',
                backgroundColor: '#ffffff',
                justifyContent: 'center',
                alignItems:'center',
                overflow: 'hidden'
              },
              "bottomcss":{
                marginTop: 0,
              },
              "contentWrap":{
                position: 'relative',
                width: canvasWidth * 0.99,
                marginBottom: 20,
                marginTop: 5,
                marginLeft: 1,
                borderRadius: 20,
                overflow:'hidden',
                backgroundColor: '#333333',
              },
              "imgbc":{
                justifyContent: 'center',
                alignItems: 'center',
                width: canvasWidth * 0.97,
                height: canvasWidth,
                marginBottom: 4,
                marginLeft: 4,
                marginTop: 10,
                borderRadius: 20,
                overflow: 'hidden',
              },
              "tapContent1":{
                position:'absolute',
                top: 0,
                left: 0,
              },
              "tapname":{
                fontSize: 18,
                color: '#fff',
                marginLeft: 15,
                marginTop: 50,
                overflow: 'hidden',
                width: canvasWidth * 0.92,
                height: 400,
                textAlign: 'left',
              },
              "name":{
                fontSize: 20,
                color: '#fff',
                marginLeft: canvasWidth * 0.08,
                width: canvasWidth * 0.84,
                height: 30,
          			textAlign: 'center',
              },
              "subtitle":{
                fontSize: 14,
                color: '#9E9C9C',
                marginLeft: canvasWidth * 0.08,
                width: canvasWidth * 0.84,
                height: 20,
                textAlign: 'center',
              },
              "content": {
          			fontSize: 14,
          			color: '#333',
                width: canvasWidth * 0.84,
                height: screenWidth * 0.15,
          			marginLeft: canvasWidth * 0.08,
              },
              "pic1": {
                width: canvasWidth * 0.3,
                height: screenWidth * 0.3,
                marginTop: canvasWidth * 0.1,
                marginLeft: canvasWidth * 0.35,
          			marginBottom: canvasWidth * 0.05,
          			borderRadius: screenWidth * 0.14,
          			overflow: 'hidden',
              },
              "pic2": {
                width: canvasWidth ,
                height: canvasWidth ,
                marginTop:10,
              },
              "bottom":{
                width: canvasWidth,
                height: screenWidth * 0.2,
                flexDirection: 'row',
                justifyContent: 'self-start',
                alignItems: 'center',
          			backgroundColor: '#fafafa',
                position: 'absolute',
                bottom: 0,
                left: 0,
              },
          		"qr": {
          		  width: canvasWidth * 0.14,
          		  height: screenWidth * 0.14,
          		  marginLeft: canvasWidth * 0.04,
          			marginRight: canvasWidth * 0.04,
          		},
              "msg": {
                fontSize: 14,
                color: '#a1a1a1',
                width: canvasWidth * 0.74,
          			height: 14,
          			textAlign: 'left'
              },
            }
          }
          

           uniapp使用wxml-to-canvas开发小程序保存canvas图片


          六、生成的效果图

           uniapp使用wxml-to-canvas开发小程序保存canvas图片


          ...... 全 剧 终 ......

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码