欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

編程日歷小程序,對(duì)小程序云開發(fā)和生成分享海報(bào)的實(shí)踐 - 新聞資訊 - 云南小程序開發(fā)|云南軟件開發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開發(fā)/軟件開發(fā)

知識(shí)

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們?cè)谧非笃湟曈X表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營(yíng)銷的便利,運(yùn)營(yíng)的高效,讓網(wǎng)站成為營(yíng)銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏?jí)提供便捷的支持!

您當(dāng)前位置>首頁 » 新聞資訊 » 小程序相關(guān) >

編程日歷小程序,對(duì)小程序云開發(fā)和生成分享海報(bào)的實(shí)踐

發(fā)表時(shí)間:2021-2-3

發(fā)布人:葵宇科技

瀏覽次數(shù):78

1、起源

朋友圈曬的很多的一本日歷書《了不起的程序員 2021》,我也買了,很厚,紙質(zhì)書嘛,現(xiàn)在已經(jīng)很少看了,加上這是一本日歷書,希望是每天都打開看。可實(shí)際上的情況是,要么忘記看今天的內(nèi)容,要么一口氣看了好幾天的內(nèi)容,然后剩下幾天又不看了。

后來《了不起的程序員 2021》在 Github 開源了。

于是乎!我就想做一個(gè)小程序,因?yàn)槭謾C(jī)每天打開的頻率太高了,碎片時(shí)間也很多,加上小程序的不用安裝用完即走的優(yōu)點(diǎn),使用方便,不會(huì)有壓力感。

再加上自己還沒有一款正兒八經(jīng)的小程序作品,對(duì)現(xiàn)在很火的云開發(fā)也沒怎么用過,特別是小程序云開發(fā),他他到底用起來爽不爽呢?(很爽?。?/p>

于是乎!開干!

2、產(chǎn)品設(shè)計(jì)

這是最傷腦筋的部分,小程序到底要做成什么樣,畫個(gè)原型圖?作為一個(gè)『資深』程序員,從來沒正經(jīng)畫過原型和設(shè)計(jì)。手足無措,改用什么工具?雖然我知道有 Sketch 這個(gè)神器,還很多在線設(shè)計(jì)工具,比如磨刀,但從來沒用過啊,最后硬著頭皮用磨刀畫了畫原型,很簡(jiǎn)陋的原型,就是線框圖級(jí)別。

這個(gè)過程不斷有新的想法,所以改來該去,產(chǎn)品設(shè)計(jì)花了好幾天,學(xué)習(xí)怎么畫原型,實(shí)現(xiàn)腦子里亂七八糟的各種想法。

在這個(gè)過程中我不斷的給自己家需求,一度增加了什么歷史上的今天、知乎日歷等等各種內(nèi)容,最后還是被自己狠心一一斃掉了,只留下純粹的編程日歷內(nèi)容。

鑒于對(duì)產(chǎn)品和設(shè)計(jì)不擅長(zhǎng),在此誠(chéng)邀 UI、產(chǎn)品小伙伴,一起租一個(gè)團(tuán)隊(duì),有機(jī)會(huì)一起做一些產(chǎn)品,讓我們的想法能落地,生根發(fā)芽。

3、開發(fā)

產(chǎn)品設(shè)計(jì)階段和開發(fā)階段占用的時(shí)間比大概是 8:2 左右,有了原型開發(fā)很快,畢竟也沒什么復(fù)雜的東西。

下面重點(diǎn)說一下分享海報(bào)功能的實(shí)現(xiàn)吧。

3.1、選擇海報(bào)分享方案

在開發(fā)分享海報(bào)功能之前我也看了下網(wǎng)上大致的方案,最后我選擇了微信小程序自己的擴(kuò)展組件:wxml-to-canvas,小程序內(nèi)通過靜態(tài)模板和樣式繪制 canvas ,導(dǎo)出圖片,可用于生成分享圖等場(chǎng)景。

我為什么不用其他方案:

  • 手寫 canvas,太麻煩
  • 后端生成前端獲取,太麻煩,我這個(gè)小程序很簡(jiÇŽn)單沒必要
  • 開源小程序海報(bào)組件,嘗試過一個(gè),感覺也不太好用,有些沒文檔用起來吃力

上圖,是騾子是馬拉出來遛遛,下圖的的海報(bào)就是通過 wxml-to-canvas 動(dòng)態(tài)繪制的。

3.2、引入 wxml-to-canvas 組件

wxml-to-canvas 的限制很多,第一次沒經(jīng)驗(yàn)的話覺得很難用,如果再讓我做一次,我就快很多了。

官方的示例只單純教你怎么生成海報(bào),缺乏上下文和怎么整合進(jìn)你的項(xiàng)目及邏輯,需要費(fèi)一下腦子。

Step1. npm 安裝,參考 小程序 npm 支持

npm install --save wxml-to-canvas
復(fù)制代碼

Step2. JSON 組件聲明

{
  "usingComponents": {
    "wxml-to-canvas": "wxml-to-canvas"
  }
}
復(fù)制代碼

Step3. wxml 引入組件

<view class="share-image-container">
  <wxml-to-canvas
    id="canvas"
    width="{{canvasWidth}}"
    height="{{canvasHeight}}"
  ></wxml-to-canvas>
</view>
復(fù)制代碼

3.3、海報(bào)分享邏輯說明

點(diǎn)擊編程日歷小程序底部的海報(bào)分享按鈕,在當(dāng)前頁面生成 canvas 預(yù)覽圖,然后再生成圖片跳轉(zhuǎn)到海報(bào)圖片預(yù)覽和保存頁面。

上面的 .share-image-container 類如下:

.share-image-container {
  border: 1px solid red;
  position: absolute;
  transform: translateY(-1000%);
  bottom: 0;
  z-index: 0;
}
復(fù)制代碼

即在頁面外生成 canvas,也是在這里調(diào)試 wxml-to-canvas 組件效果的地方,去掉該類的樣子如下:

3.4、js 獲取實(shí)例

Step4. js 獲取實(shí)例

import RenderCodeToWXML from "./renderCodeWXML.js";

Page({
  data: {
    canvasWidth: 373,
    canvasHeight: 720,
    bannerImgHeight: 240,
    bannerImgWdith: 320,
  },
  renderToCanvas() {
    wx.showLoading({
      title: "處理中...",
    });
    this.canvas = this.selectComponent("#canvas");
    const {
      canvasWidth,
      canvasHeight,
      bannerImgWdith,
      bannerImgHeight,
    } = this.data;
    let renderToWXML = new RenderCodeToWXML(
      canvasWidth,
      canvasHeight,
      bannerImgWdith,
      bannerImgHeight
    );

    const wxml = renderToWXML.renderWXML();
    const style = renderToWXML.renderStyle();
    const p1 = this.canvas.renderToCanvas({ wxml, style });
    p1.then((res) => {
      // console.log('container', res.layoutBox)
      app.globalData.container = res;
      this.container = res;
      this.extraImage();
    }).catch((err) => {
      wx.hideLoading();
      console.log("err", err);
    });
  },
  extraImage() {
    const p2 = this.canvas.canvasToTempFilePath();
    p2.then((res) => {
      wx.hideLoading();
      // app.globalData.share = res
      wx.navigateTo({
        url: "../shareImage/shareImage",
        success: function(res2) {
          // 通過eventChannel向被打開頁面?zhèn)魉蛿?shù)據(jù)
          res2.eventChannel.emit(
            "acceptDataFromOpenerPage",
            {
              share: res,
              container: app.globalData.container,
              tab: app.globalData.tab,
              date: app.globalData.dateInfo.strings,
            }
          );
        },
      });
    }).catch((err) => {
      wx.hideLoading();
      wx.showToast({
        title: err,
        icon: "none",
      });
    });
  },
});
復(fù)制代碼

這里主要就是從 renderCodeWXML.js 中獲取 WXML 和 Style,然后調(diào)用 canvas 的 renderToCanvas 方法進(jìn)行渲染:

const wxml = renderToWXML.renderWXML();
const style = renderToWXML.renderStyle();
const p1 = this.canvas.renderToCanvas({ wxml, style });
復(fù)制代碼

最后在 p1.then 里調(diào)用 this.extraImage(); 方法跳轉(zhuǎn)到下一個(gè)頁面,并通過 eventChannel.emit 方式傳遞參數(shù)。

來看看 renderCodeWXML.js 里面有什么:

const app = getApp();

export default class RenderDataToWXML {
  constructor(
    canvasWidth,
    canvasHeight,
    imgWidth,
    imgHeight
  ) {
    this.canvasWidth = canvasWidth;
    this.canvasHeight = canvasHeight;
    this.imgWidth = imgWidth;
    this.imgHeight = imgHeight;
  }

  renderWXML() {
    const { dateInfo, data, userInfo } = app.globalData;
    const openId = wx.getStorageSync("openId");
    let pData = http://www.wxapp-union.com/"";
    let pMore = "";
    let banner = "";

    if (data.data.event) {
      pData = http://www.wxapp-union.com/data.data.event.join("");
    }
    if (data.data.coding) {
      pData = http://www.wxapp-union.com/data.data.coding.join("");
    }
    if (data.data.landmark) {
      pData = http://www.wxapp-union.com/data.data.landmark.join("");
    }

    if (data.data.more) {
      pMore = data.data.more[0];
    } else if (data.data.people) {
      pMore = data.data.people[0].split(":").join(",");
    } else {
      pMore = "";
    }

    if (data.data.img) {
      banner = `
      <view class="banner">
        <image class="banner-image" mode="aspectFit" src="http://www.wxapp-union.com/${data.data.img.url}" />
      </view>`;
    }

    if (pData.length >= 156) {
      pData = http://www.wxapp-union.com/pData.substring(0, 152) + "...";
    }
    if (pMore.length >= 50) {
      pMore = pMore.substring(0, 48) + "...";
    }

    let avatar = "";
    if (userInfo && userInfo.avatarUrl) {
      avatar = `<view class="avatar">
        <image class="avatar-image" src="http://www.wxapp-union.com/${userInfo.avatarUrl}" />
        <text class="avatar-nikename">${userInfo.nickName}邀請(qǐng)你使用</text>
      </view>`;
    }

    let wxmlMore = pMore;
    if (wxmlMore) {
      wxmlMore = `
        <view>
          <text class="p-more">${pMore}</text>
        </view>
      `;
    }

    const wxml = `
      <view class="container">
        <view class="top">
          <view class="top-left">
            <view><text class="en">${dateInfo.date.monthEN}</text></view>
            <view><text class="cn">${dateInfo.lunarDate}</text></view>
          </view>
          <view><text class="top-center">${dateInfo.date.day}</text></view>
          <view class="top-right">
            <view><text class="en">${dateInfo.date.weekEN}</text></view>
            <view><text class="cn">${dateInfo.date.weekCN}</text></view>
          </view>
        </view>
        ${banner}
        <view class="middle">
          <view>
            <text class="p-data">${pData}</text>
          </view>
          ${wxmlMore}
        </view>
        <view class="qrcode">
          <view class="appinfo">
            ${avatar}
            <view><text class="appname">編程日歷</text></view>
            <view><text class="appdesc">程序員專屬日歷,最極客日歷</text></view>
          </view>
          <view class="qrcode-image">
            <image class="image" mode="aspectFit" src="https://7072-programming-calendar-3b8b7a7d082-1304448256.tcb.qcloud.la/qr/${openId}-qr.png?sign=b5a610dc6ae15c9427720ab617a2f18a&t=1609438339" />
          </view>
        </view>
      </view>
      `;
    return wxml;
  }

  // canvas樣式
  renderStyle() {
    const contentWidth = this.canvasWidth - 50;
    const mainColor = "#1296db";
    const style = {
      container: {
        width: this.canvasWidth,
        height: this.canvasHeight,
        backgroundColor: "#fff",
      },
      top: {
        width: this.canvasWidth,
        height: 82,
        backgroundColor: mainColor,
        flexDirection: "row",
        justifyContent: "space-around",
        alignItems: "center",
      },
      topLeft: {
        width: this.canvasWidth / 3,
        height: 82,
        textAlign: "center",
        alignItems: "center",
      },
      topCenter: {
        width: this.canvasWidth / 3,
        height: 82,
        lineHeight: 82,
        fontSize: 72,
        textAlign: "center",
        color: "#ffffff",
      },
      topRight: {
        width: this.canvasWidth / 3,
        height: 82,
      },
      en: {
        width: this.canvasWidth / 3,
        height: 30,
        fontSize: 20,
        textAlign: "center",
        color: "#ffffff",
        marginTop: 15,
      },
      cn: {
        width: this.canvasWidth / 3,
        height: 30,
        textAlign: "center",
        color: "#ffffff",
      },
      banner: {
        width: this.canvasWidth,
        flexDirection: "row",
        justifyContent: "center",
        marginTop: 20,
      },
      bannerImage: {
        width: this.imgWidth,
        height: this.imgHeight,
      },
      middle: {
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
        marginTop: 20,
      },
      pData: {
        width: contentWidth,
        height: 170,
        lineHeight: "1.8em",
      },
      pMore: {
        width: contentWidth,
        height: 60,
        lineHeight: "1.8em",
      },
      qrcode: {
        height: 130,
        flexDirection: "row",
        justifyContent: "space-between",
        backgroundColor: "#CCE6FF",
        paddingLeft: 20,
        paddingTop: 20,
      },
      qrcodeImage: {
        width: 90,
        height: 90,
        marginRight: 20,
        borderRadius: 45,
        flexDirection: "row",
        justifyContent: "center",
        alignItems: "center",
        backgroundColor: "#fff",
      },
      image: {
        width: 90,
        height: 90,
        scale: 0.9,
        borderRadius: 45,
      },
      appinfo: {
        flexDirection: "column",
        justifyContent: "flex-start",
        alignItems: "flex-start",
        height: 80,
      },
      avatar: {
        flexDirection: "row",
        justifyContent: "flex-start",
        width: this.canvasWidth / 1.8,
        height: 30,
      },
      avatarImage: {
        width: 30,
        height: 30,
        borderRadius: 15,
        marginRight: 5,
      },
      avatarNikename: {
        width: this.canvasWidth / 1.8,
        height: 22,
        lineHeight: 22,
        marginTop: 5,
      },
      appname: {
        width: this.canvasWidth / 2,
        height: 23,
        fontSize: 16,
        color: "#0081FF",
        marginTop: 8,
        marginLeft: 35,
      },
      appdesc: {
        width: this.canvasWidth / 2,
        height: 20,
        fontSize: 14,
        marginLeft: 35,
      },
    };
    return style;
  }

  // 省略不相關(guān)代碼
}
復(fù)制代碼

該文件就是我們畫海報(bào)的地方,就是生成 WXML 和 Style 然后導(dǎo)出 。

3.5、wxml-to-canvas 組件的注意事項(xiàng)

wxml-to-canvas 組件對(duì) wxml 模板支持有限 :

  • 支持 <view>、<text>、<image> 三種標(biāo)ç°½,通過 class 匹配 style 對(duì)象中的樣式。
  • 文字必須用 <text> 標(biāo)簽包含,否則不顯示。并且必須設(shè)置寬高。文字寬度必須先確定,超出則會(huì)自動(dòng)截?cái)?。所以?dòng)æ…‹(tài)文字可以根據(jù)å­—?jǐn)?shù),動(dòng)æ…‹(tài)設(shè)置寬度。

樣式方面:

  • 對(duì)象屬性值為對(duì)應(yÄ«ng) wxml 標(biāo)簽的 cass 駝峰形式。需為每個(gè)元素指定 width å’Œ height 屬性,否則會(huì)å°Ž(dÇŽo)致布局錯(cuò)誤。
  • 存在多個(gè) className 時(shí),位置靠后的優(yōu)先級(jí)更高,子元素會(huì)繼承父級(jí)元素的可繼承屬性。
  • 元素均為 flex 布局。left/top ç­‰ 僅在 absolute 定位下生效。

因?yàn)槲淖直仨氂?<text> 標(biāo)簽包含,并且必須設(shè)置寬高,文字寬度必須先確定,超出則會(huì)自動(dòng)截?cái)?。所以?dòng)態(tài)文字可以根據(jù)字?jǐn)?shù),動(dòng)態(tài)設(shè)置寬度。所以寫布局非常麻煩,我推薦大家為每一個(gè)元素設(shè)置背景,這樣可以看到元素渲染的范圍和寬高。如下所示:

borderColor/marginBottom/marginTop 可使用,雖然微信文檔中沒寫。

3.6、海報(bào)預(yù)覽和下載頁面

生成 canvas 并調(diào)用接口生成圖片后,我們攜帶參數(shù)跳轉(zhuǎn)到下一個(gè)頁面,先來看看 WXML,非常簡(jiǎn)單:

<view>
  <view class="share-container">
    <image
      src="{{src}}"
      mode="widthFix"
      class="image"
      style="height: {{height}}px;"
    ></image>
  </view>
  <view class="save-button">
    <van-button
      bind:tap="saveImage"
      block
      round
      icon="down"
      size="large"
      type="info"
      >保存到手機(jī)</van-button
    >
  </view>
</view>
復(fù)制代碼

js 邏輯

const app = getApp();
Page({
  data: {
    src: "",
    date: "",
    width: "",
    height: "",
  },
  onLoad() {
    const eventChannel = this.getOpenerEventChannel();
    eventChannel.on("acceptDataFromOpenerPage", (data) => {
      // console.log("data", data)
      this.setData({
        showPopup: true,
        date: data.date,
        src: data.share.tempFilePath,
        width: data.container.layoutBox.width,
        height: data.container.layoutBox.height,
      });
    });
  },
  getDatestr() {
    const { strings } = app.globalData.dateInfo;
    return strings;
  },
  saveImage() {
    wx.showLoading({
      title: "處理中...",
    });
    const _this = this;
    wx.getSetting({
      success(res) {
        if (!res.authSetting["scope.writePhotosAlbum"]) {
          wx.authorize({
            scope: "scope.writePhotosAlbum",
            success() {
              _this.save();
            },
            fail() {
              wx.showToast({
                title: "授權(quán)失敗",
                icon: "none",
              });
            },
          });
        } else {
          _this.save();
        }
      },
    });
  },
  save() {
    wx.saveImageToPhotosAlbum({
      filePath: this.data.src,
      success() {
        wx.showToast({
          title: "保存成功",
          icon: "none",
        });
      },
      fail() {
        wx.showToast({
          title: "保存失敗",
          icon: "none",
        });
      },
    });
  },
});
復(fù)制代碼

以上就是我開發(fā)海報(bào)功能的邏輯和代碼,僅供參考吧,如果你有相關(guān)經(jīng)驗(yàn)歡迎討論交流,留下你的真知灼見吧。

4、編程日歷小程序頁面截圖

最后,分幾張小程序的頁面截圖

預(yù)覽預(yù)覽

5、后續(xù)迭代計(jì)劃

增加用戶等級(jí)計(jì)劃、對(duì)應(yīng)等級(jí)可以換一些禮物,其余的。。。你有什么想法?歡迎交流!


作者:杭州程序員張張
來源:掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

相關(guān)案例查看更多

相關(guān)閱讀