微信小程序分享海報開發總結 - 新聞資訊 - 雲南小程序開發|雲南軟件開發|雲南網站建設-昆明融晨信息技術有限公司

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不(bù)管是(shì)網站,軟件還是(shì)小程序,都要(yào / yāo)直接或間接能爲(wéi / wèi)您産生價值,我們在(zài)追求其視覺表現的(de)同時(shí),更側重于(yú)功能的(de)便捷,營銷的(de)便利,運營的(de)高效,讓網站成爲(wéi / wèi)營銷工具,讓軟件能切實提升企業内部管理水平和(hé / huò)效率。優秀的(de)程序爲(wéi / wèi)後期升級提供便捷的(de)支持!

您當前位置>首頁 » 新聞資訊 » 小程序相關 >

微信小程序分享海報開發總結

發表時(shí)間:2021-1-5

發布人(rén):融晨科技

浏覽次數:72

效果展示

1111.png

繪制流程簡述
  • 請求海報數據
  • 加載圖片
  • 初始化canvas
  • 繪制文字
  • 繪制區塊
  • 微信小程序碼獲取
  • 繪制圖片
  • 導出(chū)圖片内容
  • 小程序碼解析
繪制關鍵點

Canvas 初始化

正常情況下,1px在(zài)屏幕上(shàng)就(jiù)顯示1px,但是(shì)在(zài)高清屏幕上(shàng),這(zhè)個(gè)情況就(jiù)不(bù)一樣了(le/liǎo),以(yǐ)iPhone4S爲(wéi / wèi)例,它的(de)devicePixelRatio爲(wéi / wèi)2,将看到(dào)100px的(de)邏輯值等于(yú)200px的(de)設備值。在(zài)元素的(de)邏輯像素寬度上(shàng)創建圖像,當它們被繪制出(chū)來(lái)時(shí),它們會被設備像素按比例放大(dà),并且會變得模糊。解決這(zhè)個(gè)問題的(de)方法是(shì)創建按devicePixelRatio縮放的(de)圖像,然後使用CSS按相同的(de)比例縮小.

canvas 的(de)width 和(hé / huò) height控制元素位圖的(de)屬性,如不(bù)設置默認爲(wéi / wèi)width=300,height=150

// 獲取canvas
function getCanvas() {
    return new Promise((resolve, reject) => {
        Taro.createSelectorQuery()
            .select('#myCanvas')
            .fields({
                node: true,
                size: true
            })
            .exec(res => {
                res ? resolve(res[0]) : reject(null);
            });
    });
}
/**
 * 初始化canvas并返回 ctx
 *
 * @param {Object} res
 * @returns {*}
 */
function setContext(res: Object): any {
    let {canvas, width, height} = res;
    const ctx = canvas.getContext('2d');
   
    const dpr = Taro.getSystemInfoSync().pixelRatio;
    // 防止重複放大(dà)處理
    if (canvas.width !== width * dpr) {
       // 設置畫布繪畫尺寸 = CSS尺寸 * 設備像素比率。
        canvas.width = width * dpr;
        canvas.height = height * dpr;
       // 通過dpr縮放所有繪圖操作
        ctx.scale(dpr, dpr);
    }

    return ctx;
}

let {node: canvas, width, height} = await getCanvas();
let ctx = setContext({canvas, width, height});
// 繪制前先清除,以(yǐ)防被原有圖案影響
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = '#ffffff';
複制代碼

canvas标簽,微信對Canvas做了(le/liǎo)同層渲染的(de)優化,特别注意不(bù)要(yào / yāo)将canvas标簽放到(dào)子(zǐ)組件,必須放到(dào)page頁,否則無法獲取的(de)canvas對象

<Canvas type='2d' id='myCanvas' style='width: 414px; height: 736px;' />
複制代碼

微信小程序碼獲取
  • 接口 A: 适用于(yú)需要(yào / yāo)的(de)碼數量較少的(de)業務場景
    • 生成小程序碼,可接受 path 參數較長,生成個(gè)數受限,請謹慎使用。
  • 接口 B:适用于(yú)需要(yào / yāo)的(de)碼數量極多的(de)業務場景
    • 生成小程序碼,可接受頁面參數較短,生成個(gè)數不(bù)受限。
  • 接口 C:适用于(yú)需要(yào / yāo)的(de)碼數量較少的(de)業務場景
    • 生成二維碼,可接受 path 參數較長,生成個(gè)數受限。

我們使用的(de)是(shì)接口B,需要(yào / yāo)注意的(de)點

  • scene參數隻接收32的(de)長度
  • 小程序未發布時(shí),page參數必須爲(wéi / wèi)空,scene=xxxx,才可以(yǐ)獲取到(dào)二維碼
  • 接口返回的(de)數據需要(yào / yāo)爲(wéi / wèi)base64格式方便canvas繪制時(shí)使用

圖片繪制前獲取
Taro.getImageInfo({
    src: imgUrl
}).than(res => {
    let { path } = res;
    let imgtag = canvas.createImage();
    imgtag.src = http://www.wxapp-union.com/path;
    imgtag.onload = res => {
        ctx.save();
        ctx.drawImage(imgtag, 26, 615, 2 * 27, 2 * 27);
        ctx.restore();
    };
});
複制代碼
圖片裁剪
/**
 * 畫圓
 *
 * @export
 * @param {IDrawRound} options
 * @example
 * drawRound({
 *     ctx,
 *     r: 19,
 *     x: 22,
 *     y: 15,
 *     next() {
 *         ctx.fillStyle = '#F5F5F5';
 *         ctx.fill();
 *     }
 * });
 */
export function drawRound(options: IDrawRound) {
    let { ctx, r, x, y, next } = options;
    ctx.save(); // 保存之(zhī)前的(de)
    let cx = x + r; // 圓弧坐标x
    let cy = y + r; // 圓弧坐标 y
    ctx.arc(cx, cy, r, 0, 2 * Math.PI);
    if (typeof next === 'function') {
        next();
    }
    ctx.restore(); // 返回上(shàng)一狀态
}
// 繪制圓形頭像
drawRound({
    ctx,
    r: 27,
    x: 26,
    y: 615,
    next() {
        ctx.clip();
        ctx.drawImage(imgtag, 26, 615, 2 * 27, 2 * 27); // 畫頭像
    }
});
複制代碼
繪制圓角矩形
/**
 * 繪制圓角矩形,需自行填充顔色
 *
 * @export
 * @param {IDrawRound} options
 * 
 * @example
 * drawRoundRect({
 *     ctx,
 *     r: 13,
 *     x: 26,
 *     y: 80,
 *     w: 361,
 *     h: 361,
 *     next() {
 *         ctx.clip();
 *         ctx.drawImage(imgtag, 26, 80, 361, 361);
 *     }
 * });
 */
export function drawRoundRect(options: IDrawRoundRect) {
    let {ctx, x, y, w, h, r, next} = options;
    ctx.save();
    if (w < 2 * r) {
        r = w / 2;
    }
    if (h < 2 * r) {
        r = h / 2;
    }
    ctx.beginPath();
    ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2);
    ctx.lineTo(w - r + x, y);
    ctx.arc(w - r + x, r + y, r, Math.PI * 3 / 2, Math.PI * 2);
    ctx.lineTo(w + x, h + y - r);
    ctx.arc(w - r + x, h - r + y, r, 0, Math.PI * 1 / 2);
    ctx.lineTo(r + x, h + y);
    ctx.arc(r + x, h - r + y, r, Math.PI * 1 / 2, Math.PI);
    ctx.closePath();
    if (typeof next === 'function') {
        next();
    }
    ctx.restore(); // 返回上(shàng)一狀态
}
// 底部粉色圓角矩形
drawRoundRect({
    ctx,
    r: 15,
    x: 26,
    y: 687,
    w: 147,
    h: 23,
    next() {
        // 漸變填充
        var grd = ctx.createLinearGradient(0, 0, 200, 0);
        grd.addColorStop(0, '#E828FF');
        grd.addColorStop(0.7, '#FF1D74');
        grd.addColorStop(1, '#FFB050');
        ctx.fillStyle = grd;
        ctx.fill();
        ctx.strokeStyle = grd;
        ctx.stroke();
    }
});
複制代碼
繪制文字
/**
 * 繪制文字
 *
 * @export
 * @param {*} ctx canvas的(de) 2d 對象
 * @param {string} t 繪制的(de)文字
 * @param {number} x
 * @param {number} y
 * @param {number} w 文字寬度
 * @param {number} [l=30] 行高
 * @param {number} [limit=2] 行數
 */
export function drawText(ctx: any, t: string, x: number, y: number, w: number, l:number = 30, limit:number = 2) {
    // 參數說(shuō)明
    // ctx:canvas的(de) 2d 對象,t:繪制的(de)文字,x,y:文字坐标,w:文字最大(dà)寬度
    let chr = t.split('');
    let temp = '';
    let row = [];
    let limitIndex = 0;
    let wordsNum = 0;

    for (let a = 0; a < chr.length; a++) {
        wordsNum++;
        if (ctx.measureText(temp).width < w && ctx.measureText(temp + (chr[a])).width <= w) {
            temp += chr[a];
        }
        else {
            // 行數+1
            limitIndex++;
            if (limitIndex < limit) {
                row.push(temp);
                temp = chr[a];
            }
            else {
                break;
            }
        }
    }
    // 最後一行超出(chū)最大(dà)字數
    if (limitIndex === limit && chr.length > wordsNum) {
        temp = temp.substring(0, temp.length - 1) + '...';
    }

    row.push(temp);
    for (let b = 0; b < row.length; b++) {
        ctx.fillText(row[b], x, y + (b + 1) * l);// 每行字體y坐标間隔30
    }
};
複制代碼

繪制完成導出(chū)圖片
export async function draw() {
    let { node: canvas, width, height } = await getCanvas();
    let ctx = setContext({ canvas, width, height });
    // 繪制前先清除,以(yǐ)防原有圖案被影響
    ctx.clearRect(0, 0, width, height);
    ctx.fillStyle = '#ffffff';
    ctx.fillRect(0, 0, 414, 736);
    // 底部灰色塊
    ctx.fillStyle = '#f5f5f5';
    ctx.fillRect(0, 596, 414, 140);
    // 底部粉色圓角矩形
    drawRoundRect({
        ctx,
        r: 15,
        x: 26,
        y: 687,
        w: 147,
        h: 23,
        next() {
            // 漸變填充
            var grd = ctx.createLinearGradient(0, 0, 200, 0);
            grd.addColorStop(0, '#E828FF');
            grd.addColorStop(0.7, '#FF1D74');
            grd.addColorStop(1, '#FFB050');
            ctx.fillStyle = grd;
            ctx.fill();
            ctx.strokeStyle = grd;
            ctx.stroke();
        }
    });
    // 推薦人(rén)
    ctx.fillStyle = '#666666';
    ctx.font = '13px/1.2 PingFangSC-Regular';
    ctx.fillText(`${nickName}`, 93, 635);

    // 推薦語
    ctx.fillStyle = '#222222';
    ctx.font = '15px/1.2 PingFangSC-Regular';
    ctx.fillText(`${recommendation || '推薦你一個(gè)超值的(de)變美項目'}`, 93, 660);

    // 底部文字
    ctx.fillStyle = '#ffffff';
    ctx.font = '13px/1.2 PingFangSC-Regular';
    ctx.fillText('長按識别小程序購物', 43, 703);
  	// 省略部分圖片繪制代碼
    // 獲取圖片臨時(shí)路徑
    let { tempFilePath } = await Taro.canvasToTempFilePath({ canvas } as any);
    return Promise.resolve(tempFilePath);
};
複制代碼

以(yǐ)上(shàng)導出(chū)的(de)圖片地(dì / de)址可以(yǐ)直接給img标簽賦值


圖片保存到(dào)相冊
const res = await Taro.saveImageToPhotosAlbum({
    filePath: tempFilePath
});
if (res.errMsg === 'saveImageToPhotosAlbum:ok') {
    Taro.showToast({
        title: '保存圖片成功',
        icon: 'success',
        duration: 2000
    });
}
複制代碼

小程序碼解析

小程序碼的(de)解析後,在(zài)page裏收到(dào)的(de)參數在(zài)scene裏

componentWillMount() {
  let params = this.$router.params;
  // 通過小程序碼進入,參數在(zài)scene裏
  if (params.scene) {
    let scene = decodeURIComponent(params.scene);
    let {id} = query2parmas(scene);
    this.setState({
      goodsId: id
    });
  }
  // 通過分享卡片進入參數直接在(zài)params裏 
  if (params.id) {
    this.setState({
      goodsId: params.id
    });
  }
}

相關案例查看更多