微信小程序canvas type=2d生成海报保存到相册、文字换行溢出显示...、文字删除线、分享面板

做个简单的生成二维码海报分享,我做的时候也找简单的方法看能不能实现页面直接截图那种生成图片,原生小程序不支持,不多介绍下面有全部代码有注释、参数自行替换运行看看,有问题可以咨询我,我写的已经上线

效果如图:

 js:

// 产品详情
import {
  getProductDetails,
  getDataList,
  getShareData,
  getUnlimitedQRCode
} from "../../../../../api/dsxapi";
const ui = require("../../../../../utils/ui");

Page({
  data: {
    id: null, //跳转传过来的产品id
    datas: null, //详情数据
    images: null, //轮播图
    current: 0,
    proTags: null, //产品标签
    images2: null, //详情图片
    routeId: null, //哪里打开的
    show: false, //遮罩层
    showShare: false, //分享面板
    shareImg: false, //控制分享图标
    options: [{
        name: '微信',
        icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
        openType: 'share'
      },
      {
        name: '生成分享图',
        icon: 'https://wx.applet.style.51dsx.cn/img/share_button_fxt.png'
      },
    ],
    posterDatas: {
      width: 281, //画布宽度
      height: 460, //画布高度
      // 缓冲区,无需手动设定
      pic: null,
      buttonType: 1,
      show: false, // 显示隐藏跳转设置
      success: false, // 是否成功生成过海报
      canvas: null, // 画布的节点
      ctx: null, // 画布的上下文
      dpr: 1, // 设备的像素比
    },
    QRcodeImg: null, //小程序二维码
    photoWidth: null //产品图片宽度
  },

  onLoad(options) {
    this.getlength()
    this.setData({
      id: options.id
    })
    if (options.routeId) {
      this.setData({
        routeId: options.routeId
      })
    }
    if (options.scene) { //扫二维码进来的
      this.setData({
        id: decodeURIComponent(options.scene).split('id=')[1].split('&')[0],
        routeId: decodeURIComponent(options.scene).split('routeId=')[1]
      })
    }
    let that = this;
    //生成海报初始化
    let posterDatas = that.data.posterDatas
    const query = wx.createSelectorQuery()
    query.select('#firstCanvas').fields({
        node: true,
        size: true
      },
      function (res) {
        const canvas = res.node
        const ctx = canvas.getContext('2d')
        const dpr = wx.getSystemInfoSync().pixelRatio
        canvas.width = posterDatas.width * dpr
        canvas.height = posterDatas.height * dpr
        ctx.scale(dpr, dpr)
        posterDatas.canvas = canvas
        posterDatas.ctx = ctx
        posterDatas.dpr = dpr
        //存储
        that.setData({
          posterDatas
        })
      }).exec()
    this._getProductDetails(this.data.id)
  },
  onShow() {
    // 判断分享打开并且没登录的跳转登录
    if (this.data.routeId) {
      if (wx.getStorageSync("appuser") == undefined || wx.getStorageSync("appuser") == '') {
        wx.navigateTo({
          url: "/subPackag/pages/me_jump/login/login",
        });
      }
    }
    this.getlength()
    this._getProductDetails(this.data.id)
    this.setData({
      show: false
    })
  },

  //自定义导航栏计算
  getlength() {
    let windowWidth = wx.getSystemInfoSync().windowWidth;
    const statusBarHeight = wx.getSystemInfoSync().statusBarHeight;
    const menuButton = wx.getMenuButtonBoundingClientRect();
    let navHeight = (menuButton.height + (menuButton.top - statusBarHeight) * 2) * (750 / windowWidth);
    let statusBarTop = statusBarHeight * (750 / windowWidth);
    this.setData({
      navHeight: navHeight + statusBarTop,
      statusBarTop: menuButton.top,
    })
  },
  //自定义返回按钮
  onBack() {
    if (this.data.routeId && this.data.routeId == 5 || this.data.routeId == 797 || this.data.routeId == 6) { //5好友里打开797朋友圈打开1未登录点击登录后大师兄页面返回
      // wx.navigateTo({
      //   url: `/subPackag/pages/consult_jump/EnterprisePiece/market/market`,
      // });
      // 判断从分享和扫码打开点击为关闭小程序
      wx.exitMiniProgram({
        success: function () {},
        fail: function () {}
      })
    } else {
      wx.navigateBack();
    }
  },

  //跳转企业/园区详情
  goEnterprise(event) {
    if (this.data.datas.type == 2) {
      //园区
      wx.navigateTo({
        url: `/subPackag/pages/consult_jump/EnterprisePiece/park/park?id=${event.currentTarget.dataset.id}`,
      });
    } else {
      //企业
      wx.navigateTo({
        url: `/subPackag/pages/consult_jump/EnterprisePiece/enterprise/enterprise?id=${event.currentTarget.dataset.id}`,
      });
    }
  },
  //跳转案例详情
  goCase(event) {
    let params = {
      type: 4,
      id: event.currentTarget.dataset.id,
    };
    getShareData(params)
      .then((res) => {
        wx.navigateTo({
          url: `/sDsxPackag/pages/webview/webview?url=${res.data.shareUrl}&title=${res.data.shareTitle}`
        })
      })
  },

  //跳转大师兄详情
  goDSXdetails(event) {
    getDataList({
      brotherName: event.currentTarget.dataset.item.realName,
      current: 1,
      size: 10
    }).then((res) => {
      console.log(1111111111111, res);
      const nowdata = {
        item: res.data.records[0],
      };
      console.log(111, nowdata);
      var queryBean = JSON.stringify(nowdata);
      if (wx.getStorageSync('queryBean') !== undefined) {
        let qb = wx.getStorageSync("queryBean");
        qb = queryBean;
        wx.setStorageSync("queryBean", qb);
      } else {
        wx.setStorageSync("queryBean", queryBean);
      }
      wx.navigateTo({
        url: `/sDsxPackag/pages/dsx/dsxcard/dsxcard?queryBean=${encodeURIComponent(
            queryBean
          )}`,
      });
    });
  },

  //产品详情
  _getProductDetails(id) {
    let params = {
      id: id
    };
    getProductDetails(params).then((res) => {
      this.setData({
        datas: res.data,
        images: res.data.proImgs.split(","),
        proTags: res.data.proTags.split(","),
        images2: res.data.proDetailsImgs.split(","),
        shareImg: true
      });
    }).catch(function (imError) {
      console.log(imError);
    })
  },

  //客服电话
  servicePhone() {
    if (this.data.datas.phone) {
      wx.makePhoneCall({
        phoneNumber: this.data.datas.phone
      })
    } else {
      wx.makePhoneCall({
        phoneNumber: '4001512051'
      })
    }
  },

  //轮播图预览
  tapAvatar(event) {
    console.log(event);
    wx.previewImage({
      current: event.currentTarget.dataset.item,
      urls: this.data.images,
    })
    console.log(event);
  },

  //轮播数字指示
  swiperChange(e) {
    var that = this;
    if (e.detail.source == 'touch') {
      that.setData({
        current: e.detail.current
      })
    }
  },
  //获取二维码
  //产品详情
  _getUnlimitedQRCode() {
    let params = {
      id: this.data.id,
      type: 1,
      envVersion: 'release' //正式版为 "release",体验版为 "trial",开发版为 "develop
    };
    getUnlimitedQRCode(params).then((res) => {
      const base64 = res.data.qrcode;
      const time = new Date().getTime();
      //USER_DATA_PATH:文件系统中的用户目录路径 (本地路径)
      const imgPath = wx.env.USER_DATA_PATH + "/poster" + time + "" + ".png";
      const imageData = base64.replace(/^data:image\/\w+;base64,/, "");
      const file = wx.getFileSystemManager();
      file.writeFileSync(imgPath, imageData, "base64");
      console.log(imgPath);
      this.setData({
        QRcodeImg: imgPath,
      });
    }).catch(function (imError) {
      console.log(imError);
    })
  },

  //引导打开相册权限取消按钮
  onClickHide1() {
    let posterDatas = this.data.posterDatas;
    posterDatas["buttonType"] = 1;
    this.setData({
      show: false,
      showShare: false,
      posterDatas,
      options: [{
          name: '微信',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
          openType: 'share'
        },
        {
          name: '生成分享图',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_fxt.png'
        },
      ],
    });
  },

  //分享按钮
  onClick(event) {
    if (!this.data.QRcodeImg) {
      //获取二维码
      this._getUnlimitedQRCode()
    }

    //获取产品图片宽度
    wx.getImageInfo({
      src: this.data.datas.proCover,
      success: res => {
        this.setData({
          photoWidth: res.width
        })
      }
    })
    this.setData({
      showShare: true,
      options2: [{
          name: '微信',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
          openType: 'share'
        },
        {
          name: '生成分享图',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_fxt.png'
        },
      ],
    });
  },

  //隐藏分享面板
  onClose() {
    this.onIsCanvas()
    this.data.posterDatas["buttonType"] = 1;
    this.setData({
      showShare: false,
      options: [{
          name: '微信',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
          openType: 'share'
        },
        {
          name: '生成分享图',
          icon: 'https://wx.applet.style.51dsx.cn/img/share_button_fxt.png'
        },
      ],
    });
  },

  //分享面板里面的点击事件
  onSelect(event) {
    console.log(event);
    if (event.detail.index == 0) {
      this.setData({
        showShare: false
      })
      this.onIsCanvas()
    } else if (event.detail.index == 1) {
      if (this.data.posterDatas.buttonType == 1) {
        if (this.data.QRcodeImg) {
          this.data.posterDatas["buttonType"] = 2;
          this.onBuildPosterSaveAlbum()
          this.setData({
            options: [{
                name: '微信',
                icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
                openType: 'share'
              },
              {
                name: '保存到相册',
                icon: 'https://wx.applet.style.51dsx.cn/img/share_button_down.png'
              },
            ]
          })
        }
      } else if (this.data.posterDatas.buttonType == 2) {
        this.onDownloadImges()
        this.setData({
          options: [{
              name: '微信',
              icon: 'https://wx.applet.style.51dsx.cn/img/share_button_wechat.png',
              openType: 'share'
            },
            {
              name: '保存到相册',
              icon: 'https://wx.applet.style.51dsx.cn/img/share_button_down.png'
            },
          ]
        });
      } else if (this.data.posterDatas.buttonType == 3) {
        let posterDatas = this.data.posterDatas;
        posterDatas["show"] = false;
        this.setData({
          posterDatas,
          show: true
        })
      }
    }
  },
  //海报生成
  onBuildPosterSaveAlbum() {
    let that = this;
    let posterDatas = that.data.posterDatas
    let canvas = posterDatas.canvas
    let ctx = posterDatas.ctx
    //已生成过海报的直接显示弹窗
    if (posterDatas.success) {
      posterDatas["show"] = true;
      that.setData({
        posterDatas
      })
      return;
    }
    posterDatas.show = true;
    that.setData({
      posterDatas
    })
    wx.showLoading({
      title: '海报生成中',
      mask: true
    });
    //二维码
    let promise1 = new Promise(function (resolve, reject) {
      const photo = canvas.createImage();
      photo.src = that.data.QRcodeImg;
      photo.onload = (e) => {
        resolve(photo);
      }
    });
    // 背景图
    let promise2 = new Promise(function (resolve, reject) {
      const photo = canvas.createImage();
      photo.src = "https://wx.applet.style.51dsx.cn/img/share_ig_bg.png";
      photo.onload = (e) => {
        resolve(photo);
      }
    });
    // 产品图
    let promise3 = new Promise(function (resolve, reject) {
      const photo = canvas.createImage();
      photo.src = that.data.datas.proCover;
      photo.onload = (e) => {
        resolve(photo);
      }
    });
    //获取图片信息
    Promise.all(
      [promise1, promise2, promise3]
    ).then(res => {
      //背景图
      ctx.drawImage(res[1], 0, 0, posterDatas.width, posterDatas.height);
      // 产品图
      // ctx.drawImage(res[2], 18, 18, posterDatas.width-36, 245);
      ctx.drawImage(res[2], 0, 0, that.data.photoWidth, that.data.photoWidth, 18, 18, posterDatas.width - 36, posterDatas.width - 36);
      //二维码
      ctx.drawImage(res[0], posterDatas.width - 82, posterDatas.height - 82, 64, 64);
      if (that.data.datas.payType == 2) {
        //面议
        ctx.font = "bold 20px sans-serif";
        ctx.fillStyle = "#EF3822";
        ctx.fillText('面议', 18, 290);
        ctx.fill();
      } else {
        //现价
        ctx.font = "14px"; //字体大小
        ctx.fillStyle = "#EF3822"; //字体颜色
        ctx.fillText('¥', 18, 290);
        ctx.font = "bold 20px sans-serif";
        ctx.fillStyle = "#EF3822";
        const proPrice = ctx.measureText(that.data.datas.proPrice)
        ctx.fillText(that.data.datas.proPrice, 26, 290);
        ctx.fill();
        // 原价
        let text = '¥' + that.data.datas.proOriginalPrice
        ctx.font = "10px sans-serif";
        ctx.fillStyle = "#9A9A9A";
        ctx.fillText(text, proPrice.width + 32, 290);
        ctx.fillStyle = '#9A9A9A';
        ctx.beginPath();
        const textWidth = ctx.measureText(text).width;
        ctx.rect(proPrice.width + 32, 286, textWidth, 1);
        ctx.fill();
      }
      //地区
      ctx.font = "10px sans-serif";
      ctx.fillStyle = "#9A9A9A";
      //画布宽度减去文字长度
      ctx.fillText(that.data.datas.serviceAreaText, posterDatas.width - ctx.measureText(that.data.datas.serviceAreaText).width - 18, 290);
      ctx.fill();
      //标题
      ctx.fillStyle = "#333333";
      ctx.font = "bold 14px sans-serif";
      // ctx.fillText('专精特新企业股权融资方案设计', 18, 310);
      that.toFormateStr(ctx, that.data.datas.proName, 245, 1, 18, 312, 16, 1) // 绘制文字并换行
      ctx.fill();
      //板块
      ctx.font = "11px sans-serif";
      ctx.fillStyle = "#646464";
      ctx.fillText(that.data.datas.proDesc, 18, 332);
      ctx.fill();
      //机构信息
      ctx.font = "11px";
      ctx.fillStyle = "#9D9D9D";
      ctx.fillText('机构信息:' + that.data.datas.institutionName, 18, 352);
      ctx.fill();
      //线条
      ctx.save();
      ctx.rect(18, 390, 140, 0.5);
      ctx.strokeStyle = "#9A9A9A"
      ctx.fill();

      ctx.font = "10px";
      ctx.fillStyle = "#9D9D9D";
      ctx.fillText('实际价格以扫码页面展示为准', 18, 410);
      ctx.fill();
      ctx.font = "10px";
      ctx.fillStyle = "#9D9D9D";
      ctx.fillText('长按识别查看、联系', 18, 426);
      ctx.fill();
      // 关闭loading
      wx.hideLoading();
      //显示海报
      posterDatas.success = true;
      that.setData({
        posterDatas
      })
    }).catch(err => {
      console.log(err)
      wx.hideLoading();
      wx.showToast({
        icon: 'none',
        title: '海报生成失败,请稍后再试.',
      })
    })
  },

  // 文字换行
  toFormateStr(ctx, str, draw_width, lineNum, startX, startY, steps, number) {
    //ctx:canvas的 2d 对象,str:绘制的文字,startX,startY:文字坐标,draw_width:文字最大宽度,lineNum:需要的行数,steps:行高,number:减少最后几个字变成...
    let strWidth = ctx.measureText(str).width; // 测量文本源尺寸信息(宽度)
    let startpoint = startY,
      keyStr = '',
      sreLN = strWidth / draw_width; // 文本长度除以换行的宽 得到一共生成多少行
    let liner = Math.ceil(sreLN); // 计算文本源一共能生成多少行
    let strlen = parseInt(str.length / sreLN); // 等比缩放测量一行文本显示多少个字符
    // 若文本不足一行,则直接绘制,反之大于传入的最多行数(lineNum)以省略号(...)代替
    if (strWidth < draw_width) {
      ctx.fillText(str, startX, startpoint);
    } else {
      for (let i = 1; i < liner + 1; i++) {
        let startPoint = strlen * (i - 1);
        if (i < lineNum || lineNum == -1) {
          keyStr = str.substr(startPoint, strlen);
          ctx.fillText(keyStr, startX, startpoint);
        } else {
          keyStr = str.substr(startPoint, strlen - number) + '...';
          ctx.fillText(keyStr, startX, startpoint);
          break;
        }
        startpoint = startpoint + steps;
      }
    }
  },

  //画布转图片
  onCanvasBuildImges() {
    let that = this;
    let posterDatas = that.data.posterDatas;
    wx.canvasToTempFilePath({
      canvas: posterDatas.canvas,
      width: posterDatas.width,
      height: posterDatas.height,
      // destWidth: posterDatas.width * 3,
      // destHeight: posterDatas.height * 3,
      quality: 1,
      success: function success(res) {
        posterDatas["pic"] = res.tempFilePath;
        that.setData({
          posterDatas
        })
        that.onDownloadImges();
      },
      fail: function complete(e) {
        wx.hideLoading();
        wx.showToast({
          icon: 'none',
          title: 'sorry 保存失败,请稍后再试.',
        })
        return;
      }
    });
  },

  //下载图片
  onDownloadImges() {
    wx.showLoading({
      title: '保存中',
      mask: true
    });
    let that = this;
    let posterDatas = that.data.posterDatas;
    if (!posterDatas.pic) {
      that.onCanvasBuildImges();
      return;
    }
    wx.saveImageToPhotosAlbum({
      filePath: posterDatas.pic,
      success(res) {
        wx.hideLoading();
        wx.showToast({
          icon: 'none',
          title: '已保存到相册',
        })
        that.onIsCanvas()
        posterDatas["buttonType"] = 1;
        that.setData({
          showShare: false,
          posterDatas
        })
      },
      fail: function (res) {
        wx.hideLoading();
        wx.showToast({
          icon: 'none',
          title: '已取消',
        })
        posterDatas["buttonType"] = 3;
        that.setData({
          posterDatas
        })
        return;
      }
    })
  },

  //在打开授权设置页后回调
  onBindOpenSetting() {
    let that = this;
    let posterDatas = that.data.posterDatas;
    posterDatas["buttonType"] = 1;
    that.setData({
      posterDatas
    })
  },

  //隐藏海报
  onIsCanvas() {
    let that = this;
    let posterDatas = that.data.posterDatas;
    posterDatas["buttonType"] = 1;
    posterDatas["show"] = false;
    that.setData({
      posterDatas
    })
  },

  onShareAppMessage: function (res) { //分享给好友
    var that = this
    return {
      title: that.data.datas.proName + '—产品详情',
      path: 'subPackag/pages/consult_jump/EnterprisePiece/product/product?id=' + that.data.datas.id + '&routeId=' + 5,
      imageUrl: '',
      success: function (res) {
        console.log(res);
        wx.showToast({
          title: '分享成功',
          icon: "none"
        });
      },
      fail: function (res) {
        wx.showToast({
          title: '分享失败',
          icon: "none"
        })
      }
    }
  },
  onShareTimeline: function () { //分享朋友圈
    var tha = this
    return {
      title: tha.data.datas.proName + '—产品详情',
      query: 'id=' + tha.data.datas.id + '&routeId=' + 797,
      imageUrl: tha.data.datas.proCover,
      success: function (res) {
        wx.showToast({
          title: '分享成功',
          icon: "none"
        });
      },
      fail: function (res) {
        wx.showToast({
          title: '分享失败',
          icon: "none"
        })
      }
    }
  },
})

 wxml:

<!-- 自定义导航 -->
<view class="nav-back" style="height: {{navHeight}}rpx;">
  <image class="imgbackw" style="margin-top: {{statusBarTop}}px" src="https://wx.applet.style.51dsx.cn/img/icon_navigation_return.png" catchtap="onBack" />
  <view class="nav-name" style="margin-top: {{statusBarTop}}px">产品详情</view>
</view>

<view class="product" wx:if="{{datas}}" style="margin-top: {{navHeight}}rpx;">
  <!-- 轮播 -->
  <view wx:if="{{images[0]!==''}}" class="swiper1" style="overflow: hidden;">
    <swiper bindchange="swiperChange" class="swiper" indicator-active-color="white" current="0" indicator-color="#fff6" display-multiple-items circular="{{true}}">
      <swiper-item wx:for="{{images}}" wx:key="index">
        <image data-item="{{item}}" bindtap="tapAvatar" src="{{item}}" mode="aspectFill" />
      </swiper-item>
    </swiper>
    <view class="imageCount" wx:if="{{images.length>1}}">{{current+1}}/{{images.length}}</view>
  </view>
  <view class="middle">
    <view class="price">
      <view class="price-div">
      <!-- 价钱、地区 -->
        <view style="display: inline-block;">
          {{ datas.payType == 2 ? "" : "¥"
          }}<span class="item1">{{
            datas.payType == 2 ? "面议" : datas.proPrice
            }}</span>
          <span class="item3">{{
            datas.payType == 2 || datas.proOriginalPrice == 0
            ? ""
            : "¥" + datas.proOriginalPrice
            }}</span>
        </view>
        <view class="area">
          {{datas.serviceAreaText}}
        </view>
      </view>
      <view class="info">
        <!-- 标题、板块、标签 -->
        <view class="item1">{{ datas.proName }}</view>
        <view class="item2" wx:if="{{datas.proDesc}}">{{ datas.proDesc }}</view>
        <view wx:if="{{proTags[0]!==''}}">
          <view class="item3" wx:for="{{proTags}}" wx:key="index">
            {{item}}
          </view>
        </view>
      </view>
    </view>
    <view class="institution">
      <view class="title">机构信息</view>
      <!-- 机构信息 -->
      <view class="info2" data-id="{{datas.companyId}}" bindtap="goEnterprise">
        <image src="{{datas.institutionLogo}}" class="img1" mode="aspectFit" />
        <view class="right">
          <view class="item1">
            {{ datas.institutionName }}
          </view>
          <view class="item2">
            {{ datas.institutionAddress }}
          </view>
        </view>
      </view>
      <view wx:if="{{datas.userNames.length > 0}}">
      <!-- 大师兄左右滑动列表 -->
        <van-divider customStyle="margin:0;" />
        <view class="BigMasterBox">
          <view class="BigMasterItem" data-item="{{item}}" bindtap="goDSXdetails" wx:for="{{datas.userNames}}" wx:key="index">
            <image src="{{item.photo}}" class="img" mode="aspectFill"/>
            <view class="right">
              <view class="item1">
                {{ item.realName }}
              </view>
              <view class="{{item.online == 1 ? 'item2' : 'red'}}">
                {{
                item.online == 1
                ? "可咨询"
                : "可预约"
                }}
              </view>
              <view class="item3">
                {{ item.roleName }} |
                {{ item.unitStartTimeText }}
              </view>
            </view>
          </view>
        </view>
        <view class="prompt">
          以上大师兄由产品方所列,由用户自主选择
        </view>
      </view>
    </view>
    <view class="case" wx:if="{{datas.serviceNames.length > 0}}">
      <view class="title">服务案例</view>
      <view class="caseBox">
        <view class="caseItem" data-id="{{item.id}}" bindtap="goCase" wx:for="{{datas.serviceNames}}" wx:key="index">
          <!-- 案例左右滑动列表 -->
          <image src="https://wx.applet.style.51dsx.cn/img/store_icon_fwal.png" class="img" mode='widthFix' />
          <view class="right">
            <view class="item1">
              {{ item.customerName }}
            </view>
            <view class="item2">
              {{ item.sectionText }}
            </view>
            <view class="item2">
              {{ item.serviceTime }}年
            </view>
          </view>
        </view>
      </view>
    </view>
    <view class="details">
      <view class="median"></view>
      <span> 产品详情 </span>
      <view class="median"></view>
    </view>
  </view>
  <!-- 产品介绍长图列 -->
  <view class="introduce">
    {{ datas.proDetails }}
  </view>
  <image wx:if="{{images2[0]!==''}}" class="img3" wx:for="{{images2}}" wx:key="index" src="{{item}}" mode='widthFix' />
  <view class="bottomBox">
    <view class="FixedBottom">
      <view>
        <!-- <view class="item1" bindtap="servicePhone">
          <image class="img" src="https://image-cos.51dsx.cn/images/2023-05-08/store_button_service.png" />
          <view class="text">客服</view>
        </view> -->
        <view class="item2" data-id="{{datas.companyId}}" bindtap="goEnterprise">
          <image class="img" src="https://image-cos.51dsx.cn/images/2023-05-08/store_button_store.png" />
          <view class="text">店铺</view>
        </view>
      </view>
      <view class="item3" bindtap="servicePhone">
        联系机构
      </view>
    </view>
  </view>
</view>
<!-- 海报 -->
<view class="canvasMain" hidden="{{!posterDatas.show}}">
  <canvas type="2d" id="firstCanvas" class="firstCanvas" style="width:{{posterDatas.width}}px;height:{{posterDatas.height}}px;"></canvas>
</view>
<!-- 分享图标 -->
<image wx:if="{{shareImg}}" class="shareImg" bindtap="onClick" src="https://wx.applet.style.51dsx.cn/img/xiangqing_button_share.png" mode="aspectFill" />
<!-- 弹窗去打开相册权限 -->
<van-overlay z-index="999999" show="{{ show }}">
  <view class="wrapper">
    <view class="block" catch:tap="noop">
      <view class="popup-box">
        <view class="telephone">进入设置页,开启“保存到相册”</view>
        <van-divider customStyle="margin:0;" />
        <view class="ncontent">
          <button class='button' bindtap='onClickHide1'>取消</button>
          <button class='button' open-type='openSetting' bindopensetting='onBindOpenSetting'>确定</button>
        </view>
      </view>
    </view>
  </view>
</van-overlay>
<!-- 分享面板 -->
<van-share-sheet show="{{ showShare }}" title="" options="{{ options }}" bind:select="onSelect" bind:close="onClose" />

 wxss:

.nav-back {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  text-align: center;
  background: #fff;
  z-index: 9999999;
}

.nav-back .nav-name {
  font-size: 32rpx;
  font-family: PingFang SC-Semibold, PingFang SC;
  font-weight: 600;
  color: #000000;
  line-height: 40rpx;
  width: 100%;
  color: #000;
  height: 60rpx;
  line-height: 60rpx;
}

.imgbackw {
  width: 60rpx;
  height: 60rpx;
  position: absolute;
  left: 0rpx;
  z-index: 100;
}

.imageCount {
  padding: 4rpx 20rpx;
  font-size: 24rpx;
  border-radius: 40rpx;
  background-color: rgba(0, 0, 0, 0.6);
  color:#fff;
  position:absolute;
  right:16rpx;
  bottom:16rpx;
}
.swiper1 {
  width: 100vw;
  height: 100vw;
  transform: translateY(0);
}

/* 广告轮播图 */
.swiper {
  /*再设置个transform的属性*/
  transform: translateY(0);

}

.swiper image,
.swiper {
  width: 100vw;
  height: 100vw;
}

.swiper swiper-item {
  position: relative;
}
.product {
  padding-bottom: 164rpx;
  width: 100%;
  height: auto;
}

.product .FixedBottom .item3 {
  /* width: 2.47rpx; */
  /* width: 70%; */
  width: 88%;
  height: 88rpx;
  background: linear-gradient(138deg, #5092f7 0%, #3171e8 100%);
  border-radius: 80rpx;
  line-height: 88rpx;
  font-size: 32rpx;
  font-weight: 500;
  color: #ffffff;
}

.product .FixedBottom .img {
  width: 48rpx;
  height: 48rpx;
}

.product .FixedBottom .text {
  font-size: 24rpx;
  font-weight: 400;
  color: #000000;
  line-height: 36rpx;
}

.product .FixedBottom .item1,
.product .FixedBottom .item2 {
  font-size: 24rpx;
  color: #000000;
  line-height: 36rpx;
}

.product .FixedBottom .item1,
.product .FixedBottom .item2,
.product .FixedBottom .item3 {
  display: inline-block;
  text-align: center;
}

.product .bottomBox {
  width: 100%;
  background: #fff;
  position: fixed;
  left: 50%;
  transform: translate(-50%, 0);
  bottom: 0;
  padding: 20rpx 0rpx 52rpx 0rpx;
  border-top: 1px solid rgb(247, 246, 246);
}

.product .FixedBottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 24rpx 0rpx 40rpx;
  /* margin: 0 auto; */
}

.product .img3 {
  width: 100vw;
}

.product .introduce {
  padding: 20rpx;
  font-size: 28rpx;
  color: #444444;
  line-height: 42rpx;
}

/* 内容 价钱信息、机构、案例 */
.middle .details span {
  margin: 0 20rpx;
}

.middle .details .median {
  display: inline-block;
  width: 96rpx;
  border-bottom: #d8d8d8 2rpx solid;
}

.middle .details {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 40rpx;
}

.middle .case .caseBox .caseItem .right .item2 {
  font-size: 24rpx;
  color: #6f6f6f;
  line-height: 36rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}

.middle .case .caseBox .caseItem .right .item1 {
  font-size: 28rpx;
  font-weight: 600;
  color: #000000;
  line-height: 40rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}

.middle .case .caseBox .caseItem .right {
  margin-left: 20rpx;
}

.middle .case .caseBox .caseItem .img {
  width: 112rpx;
  min-width: 112rpx;
  height: 112rpx;
  border-radius: 12rpx;
}

.middle .case .caseBox .caseItem {
  display: flex;
  align-items: center;
  margin-right: 16rpx;
  flex-shrink: 0;
  background: #f6f6f6;
  border-radius: 8rpx;
  height: 160rpx;
  padding: 0rpx 20rpx;
  min-width: 400rpx;
  max-width: 400rpx;
}

.middle .case .caseBox .caseItem:last-child {
  margin-right: 0 !important;
}

.middle .case .caseBox {
  border-radius: 12rpx;
  /* padding: 0.12rpx 0.1rpx; */
  /* 设置超出滚动 */
  overflow-x: auto;
  display: flex;
  justify-content: space-between;
  margin-top: 20rpx;
}

.product .middle .case {
  margin-top: 16rpx;
  padding: 28rpx 20rpx 20rpx 20rpx;
  background: #fff;
  border-radius: 20rpx;
  border: 2rpx solid #ffffff;
}

::-webkit-scrollbar {
  /* 隐藏滚动条 */
  display: none;
}

.BigMasterItem .right {
  display: inline-block;
  font-size: 24rpx;
  color: #000000;
  line-height: 32rpx;
  margin-left: 8rpx;

}

.product .middle .institution .BigMasterBox .BigMasterItem .img {
  width: 60rpx;
  height: 60rpx;
  border-radius: 30rpx;
  object-fit: cover;
}

.product .middle .institution .prompt {
  margin-top: 16rpx;
  font-size: 24rpx;
  color: #9a9a9a;
  line-height: 36rpx;
}

.product .middle .institution .BigMasterBox .BigMasterItem:last-child {
  margin-right: 0 !important;
}

.product .middle .institution .BigMasterBox .BigMasterItem .item1 {
  display: inline-block;
  font-size: 28rpx;
  font-weight: 600;
  color: #444444;
  line-height: 36rpx;
}

.product .middle .institution .BigMasterBox .BigMasterItem .red {
  margin-left: 8rpx;
  display: inline-block;
  padding: 0 8rpx;
  height: 32rpx;
  background: #3a89ff;
  color: #fff;
  font-size: 24rpx;
  line-height: 32rpx;
  border-radius: 8rpx;
}

.product .middle .institution .BigMasterBox .BigMasterItem .item2 {
  margin-left: 8rpx;
  display: inline-block;
  padding: 0 8rpx;
  height: 32rpx;
  background: #4dc741;
  color: #fff;
  font-size: 24rpx;
  line-height: 32rpx;
  border-radius: 8rpx;
}

.product .middle .institution .BigMasterBox .BigMasterItem .item3 {
  margin-top: 4rpx;
  font-size: 24rpx;
  color: #9a9a9a;
  line-height: 28rpx;
}

.BigMasterItem {
  display: flex;
  align-items: center;
  margin-right: 16rpx;
  flex-shrink: 0;
  background: #f3f8ff;
  border-radius: 16rpx;
  padding: 28rpx 24rpx;
}

.BigMasterBox {
  /* 设置超出滚动 */
  overflow-x: auto;
  display: flex;
  justify-content: space-between;
  margin-top: 20rpx;
}

::v-deep .el-divider--horizontal {
  margin: 0;
  background-color: #e5e5e5 !important;
}

.product .middle .institution .info2 {
  margin-top: 24rpx;
  margin-bottom: 24rpx;
  display: flex;
  align-items: center;
}

.product .middle .institution .img1 {
  display: inline-block;
  width: 88rpx;
  min-width: 88rpx;
  max-height: 88rpx;
  border-radius: 12rpx;
  border: 1rpx solid #ededed;
}

.product .middle .institution .info2 .right {
  margin-left: 16rpx;
  display: inline-block;
}

.product .middle .institution .info2 .right .item1 {
  font-size: 32rpx;
  font-weight: 600;
  color: #000000;
  line-height: 40rpx;
}

.product .middle .institution .info2 .right .item2 {
  margin-top: 6rpx;
  font-size: 26rpx;
  color: #9a9a9a;
  line-height: 36rpx;
  min-height: 36rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}

.product .middle .institution {
  margin-top: 16rpx;
  padding: 28rpx 20rpx 20rpx 20rpx;
  background: linear-gradient(180deg, #e0edff 0%, #ffffff 23%, #ffffff 100%);
  border-radius: 20rpx;
  border: 2rpx solid #ffffff;
}

.product .middle .institution .title,
.product .middle .case .title {
  font-size: 30rpx;
  font-weight: 600;
  color: #000000;
  line-height: 40rpx;
}

.product .middle .info .item1 {
  width: 100%;
  font-size: 32rpx;
  font-weight: 600;
  color: #333333;
  line-height: 48rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.product .middle .info .item2 {
  width: 100%;
  font-size: 26rpx;
  color: #868686;
  line-height: 40rpx;
  margin-top: 8rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  margin-bottom: 8rpx;
}

.product .middle .info .item3 {
  display: inline-block;
  height: 32rpx;
  font-size: 24rpx;
  color: #9a9a9a;
  line-height: 32rpx;
  background: #f7f7f7;
  border-radius: 4rpx;
  padding: 0 12rpx;
  margin-right: 20rpx;
}

.product .middle .info .item3:last-child {
  margin-right: 0 !important;
}

.custom-indicator {
  position: absolute;
  right: 16rpx;
  bottom: 16rpx;
  padding: 4rpx 20rpx;
  font-size: 24rpx;
  border-radius: 40rpx;
  color: #fff;
  background: rgba(0, 0, 0, 0.6);
}

.swipeItem {
  height: 100vw;
  width: 100%;
}

.itemImg {
  height: 100%;
  width: 100%;
  object-fit: cover;
  background-color: #f2f2f2;
}

.product .middle {
  padding: 16rpx 12rpx 24rpx;
  background: #f7f7f7;
}

.product .middle .price {
  background: #fff1f1;
  border-radius: 20rpx;
  padding: 16rpx 0 0;
}

.product .middle .info {
  padding: 28rpx 20rpx 32rpx 20rpx;
  border-radius: 20rpx;
  background: #fff;
}

.middle .price .price-div {
  margin-bottom: 12rpx;
  margin-left: 20rpx;
  color: #ef3822;
  font-size: 28rpx;
  position: relative;
}

.middle .price .price-div .area {
  font-size: 26rpx;
  color: #808080;
  display: inline-block;
  position: absolute;
  right: 20rpx;
  top:50%;
  transform:translate(0,-50%);
  line-height: 80rpx;
}

.middle .price .price-div .item1 {
  color: #ef3822;
  font-size: 44rpx;
  font-weight: 600;
}

.middle .price .price-div .item2 {
  color: #ef3822;
  font-size: 24rpx;
}

.middle .price .price-div .item3 {
  text-decoration: line-through;
  margin-left: 20rpx;
  color: #9a9a9a;
  font-size: 24rpx;
}

/* 生成海报 */
.shareImg{
  width: 100rpx;
  height: 100rpx;
  position: fixed;
  right: 14rpx;
  top: 80%;
  z-index: 99;
}
.popup-box {
  position: fixed;
  top: 50vh;
  left: 50vw;
  transform:translate(-50%,-50%);
  width: 62vw;
  background:#fff;
  border-radius: 16rpx;
  opacity: 1;
  margin: auto;
  text-align: center;
  padding: 34rpx 32rpx 36rpx;
}
.telephone{
  margin-bottom: 20rpx;
}
.canvasMain{
  position: fixed;
  top: 8vh;
  width: 100vw;
  z-index: 999999 !important;
}
.firstCanvas{
  margin: 5vh auto 0;
  z-index: 9999 !important;
}
.ncontent{
  margin-top: 20rpx;
  display: flex;
  justify-content: space-between;
}

.van-share-sheet__cancel {
  font-size: 32rpx !important;
}

.van-share-sheet__name{
  font-size: 26rpx !important;
  color: #333333 !important;
}
.van-share-sheet__header, .van-share-sheet__options{
  background: #F5F5F5;
}
.van-share-sheet__icon {
  width: 128rpx !important;
  height: 128rpx !important;
}
.van-share-sheet__cancel:before{
  background-color: #F5F5F5 !important;
}

http://www.niftyadmin.cn/n/4958188.html

相关文章

解决LiveData数据倒灌的新思路

数据倒灌现象 对于LiveData“数据倒灌”的问题&#xff0c;我相信很多人已经都了解了&#xff0c;这里提一下。所谓的“数据倒灌”&#xff1a;其实是类似粘性广播那样&#xff0c;当新的观察者开始注册观察时&#xff0c;会把上次发的最后一次的历史数据传递给当前注册的观察…

【React】生命周期和钩子函数

概念 组件从被创建到挂载到页面中运行&#xff0c;再到组件不用时卸载的过程。 只有类组件才有生命周期。 分为三个阶段&#xff1a; 挂载阶段更新阶段销毁阶段 三个阶段 挂载阶段 钩子函数 - constructor 创建阶段触发 作用&#xff1a;创建数据 之前定义状态是简写&…

铜矿人员定位安全方案

针对铜矿中的人员定位安全需求&#xff0c;可以采用以下方案&#xff1a; 1.实时人员定位系统&#xff1a;建立一个实时人员定位系统&#xff0c;通过在矿工的工作服或安全帽上安装UWB或RFID定位设备&#xff0c;以及相应的接收器和基站&#xff0c;实时跟踪和定位矿工的位置。…

Pytorch-day09-模型微调-checkpoint

模型微调&#xff08;fine-tune)-迁移学习 torchvision微调timm微调半精度训练 起源&#xff1a; 1、随着深度学习的发展&#xff0c;模型的参数越来越大&#xff0c;许多开源模型都是在较大数据集上进行训练的&#xff0c;比如Imagenet-1k&#xff0c;Imagenet-11k等2、如果…

怎么借助ChatGPT处理数据结构的问题

目录 使用ChatGPT进行数据格式化转换 代码示例 ChatGPT格式化数据提示语 代码示例 批量格式化数据提示语 代码示例 ChatGPT生成的格式化批处理代码 使用ChatGPT合并不同数据源的数据 合并数据提示语 自动合并数据提示语 ChatGPT生成的自动合并代码 结论 数据合并是…

Day17-Node后端身份认证-JWT

Day17-Node后端身份验证 一 密码加密 1 MD5加密 创建MD5.js//node提供了一个内置模块crypto用于密码加密 const crypto = require("crypto")module.exports.getMd5 = function(password){const md5

设计模式二十:观察者模式(Observer Pattern)

定义了一种一对多的依赖关系&#xff0c;允许多个观察者&#xff08;也称为订阅者&#xff09;对象同时监听一个主题对象&#xff0c;当主题对象发生变化时&#xff0c;所有依赖于它的观察者都会收到通知并自动更新。 观察者模式的使用场景 观察者模式在许多场景中都可以发挥…

python 打印人口分布金字塔图

背景 今天介绍一个不使用 matplot&#xff0c;通过DebugInfo模块打印人口金字塔图的方法。 引入模块 pip install DebugInfo打印人口金字塔图 下面的代码构建了两个人口数据&#xff08;仅做功能演示&#xff0c;不承诺任何参考价值&#xff09;&#xff0c;男性人口和女性…