微信小程序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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/84116.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Excel带数值的计算公式

问题描述 如图&#xff0c;想实现在第三列单元格中实现带数值的计算表达式 解决方法 单元格 & "/" & 单元格 & "" & TEXT(单元格/单元格, "0.00%")& 为简单的 与 符号 最后设定单元格数值与格式&#xff08;保留两位小数…

【Rust】Rust学习 第十七章Rust 的面向对象特性

面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;是一种模式化编程方式。对象&#xff08;Object&#xff09;来源于 20 世纪 60 年代的 Simula 编程语言。这些对象影响了 Alan Kay 的编程架构中对象之间的消息传递。他在 1967 年创造了 面向对…

[MySQL]主从服务器布置

配置主服务器 配置文件 /etc/my.cnf 在[mysqld]下进行配置 log_binON //启动二进制日志 log-bin mysql-bin //启用二进制日志&#xff0c;用于记录主服务器的更新操作 server-id 1 // 用来表示mysql服务id,保证集成环境中的唯一性 , 范围 [1,2^32) read-only0 // 1表示只…

Android Studio 接入OpenCV最简单的例子 : 实现灰度图效果

1. 前言 上文 我们在Windows电脑上实现了人脸功能&#xff0c;接下来我们要把人脸识别的功能移植到Android上。 那么首先第一步&#xff0c;就是要创建一个Native的Android项目&#xff0c;并且配置好OpenGL&#xff0c;并能够调用成功。 这里我们使用的是openCV-4.8.0&#x…

SOLIDWORKS有限元分析

SOLIDWORKS是一款广泛使用的三维计算机辅助设计软件&#xff0c;同时它还具有强大的有限元分析功能。有限元分析是一种工程分析方法&#xff0c;它将复杂的实体分解成许多小的有限元素&#xff0c;以便对其进行数学建模和分析。SOLIDWORKS的有限元分析功能可以帮助工程师预测和…

在Flutter应用内部实现分屏功能

前言 这一次被要求实现屏幕上同时展示两个页面&#xff0c;并且两个页面的逻辑&#xff0c;功能互不影响&#xff0c;通俗一点讲就是在Flutter内部实现一个类似于分屏的功能&#xff0c;这可难不倒我。 方法 要在 Flutter 中实现一个屏幕的上半部分和下半部分展示不同的页面…

金融市场中的机器学习;快手推出自研语言模型“快意”

&#x1f989; AI新闻 &#x1f680; OpenAI可能面临《纽约时报》的起诉&#xff0c;侵犯知识产权引发争议 摘要&#xff1a;OpenAI使用《纽约时报》的文章和图片来训练AI模型&#xff0c;违反了《纽约时报》的服务条款&#xff0c;可能面临巨大损失。此前&#xff0c;也有其…

无涯教程-PHP - XML

简单的XML解析器解析 Name&#xff0c; attributes 和 textual content&#xff0c;简单的XML函数如下所示- simplexml_load_file() 此函数接受文件路径作为第一个参数&#xff0c;这是必需的。 simplexml_load_file(($fileName,$class,$options,$ns,$is_prefix) simplexml…

韩语字母及输入法介绍

韩语字母及输入法介绍 字母由来 朝鲜语字母的由来为如下&#xff1a;十五世纪的朝鲜王国世宗大王 和他的集贤殿大臣在思考&#xff0c;创制自己本国的文字&#xff0c;仿照了“天地人思想”和“发音器官的形象”而创制了朝鲜语字母。 ​ 元音是由三个要素而组成的&#xff1…

Confluent kafka 异常退出rd_tmpabuf_alloc0: rd kafka topic info_new_with_rack

rd_tmpabuf_alloc0: rd kafka topic info_new_with_rack 根据网上的例子&#xff0c;做了一个测试程序。 C# 操作Kafka_c# kafka_Riven Chen的博客-CSDN博客 但是执行下面一行时&#xff0c;弹出上面的异常&#xff0c;闪退。 consumer.Subscribe(queueName) 解决方案&…

TensorRT推理手写数字分类(三)

系列文章目录 &#xff08;一&#xff09;使用pytorch搭建模型并训练 &#xff08;二&#xff09;将pth格式转为onnx格式 &#xff08;三&#xff09;onxx格式转为engine序列化文件并进行推理 文章目录 系列文章目录前言一、TensorRT是什么&#xff1f;二、如何通过onnx生成en…

Java并发编程之线程池详解

目录 &#x1f433;今日良言:不悲伤 不彷徨 有风听风 有雨看雨 &#x1f407;一、简介 &#x1f407;二、相关代码 &#x1f43c;1.线程池代码 &#x1f43c;2.自定义实现线程池 &#x1f407;三、ThreadPoolExecutor类 &#x1f433;今日良言:不悲伤 不彷徨 有风听风 有…

C++图形界面编程-MFC

C控制台程序是命令行黑框&#xff0c;如果要写一个图形界面&#xff0c;VS也提供了图形界面编程MFC。建项目的时候选如下选项&#xff1a; 类似于QT。 问&#xff1a;那么MFC项目的运行入口main()或WinMain()在哪里呢&#xff1f; 答&#xff1a;其实&#xff0c;在MFC应用程…

R语言机器学习方法在生态经济学领域

近年来&#xff0c;人工智能领域已经取得突破性进展&#xff0c;对经济社会各个领域都产生了重大影响&#xff0c;结合了统计学、数据科学和计算机科学的机器学习是人工智能的主流方向之一&#xff0c;目前也在飞快的融入计量经济学研究。表面上机器学习通常使用大数据&#xf…

RunnerGo中WebSocket、Dubbo、TCP/IP三种协议接口测试详解

大家好&#xff0c;RunnerGo作为一款一站式测试平台不断为用户提供更好的使用体验&#xff0c;最近得知RunnerGo新增对&#xff0c;WebSocket、Dubbo、TCP/IP&#xff0c;三种协议API的测试支持&#xff0c;本篇文章跟大家分享一下使用方法。 WebSocket协议 WebSocket 是一种…

LeetCode863. 二叉树中所有距离为 K 的结点(相关话题:深度遍历,广度遍历)

题目描述 给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 k 。 返回到目标结点 target 距离为 k 的所有结点的值的列表。 答案可以以 任何顺序 返回。 示例 1: 输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, k = 2 输出:[7,4,1] 解释…

[技术杂谈]macOS上todesk无法远程操作鼠标键盘

远程到被控Mac后能看到画面&#xff0c;鼠标键盘操作无反应 远程后发现画面显示正常&#xff0c;但是键盘和鼠标的操作没有响应 可能是辅助功能没有勾选ToDesk_Session的权限。 可按以下步骤操作&#xff1a; 1> 在左上角点击苹果图标&#xff0c;选择“系统偏好设置” …

微服务-Ribbon(负载均衡)

负载均衡的面对多个相同的服务的时候&#xff0c;我们选择一定的策略去选择一个服务进行 负载均衡流程 Ribbon结构组成 负载均衡策略 RoundRobinRule&#xff1a;简单的轮询服务列表来选择服务器AvailabilityFilteringRule 对两种情况服务器进行忽略&#xff1a; 1.在默认情…

Dockerfile创建 LNMP 服务+Wordpress 网站平台

文章目录 一.环境及准备工作1.项目环境2.服务器环境3.任务需求 二.Linux 系统基础镜像三.docker构建Nginx1.建立工作目录上传安装包2.编写 Dockerfile 脚本3.准备 nginx.conf 配置文件4.生成镜像5.创建自定义网络6.启动镜像容器7.验证 nginx 四.docker构建Mysql1. 建立工作目录…

SELinux 入门 pt.1

哈喽大家好&#xff0c;我是咸鱼 文章《SELinux 导致 Keepalived 检测脚本无法执行》以【keepalived 无法执行检测脚本】为案例向大家简单介绍了关于 SELinux 的一些概念 比如说什么是自主访问控制 DAC 和 强制访问控制 MAC&#xff1b;SELinux 安全上下文的概念等等 那么今…