2022-10-26uniapp 小程序頁面 生成圖片后保存手機(jī)相冊(cè)

一、整體步驟流程:

1、用小程序的頁面把想要的海報(bào)格式渲染出來(有海報(bào)設(shè)計(jì)稿的話此處可以不用)。

我的海報(bào)組成:上面一張背景圖,背景圖的左下方是說明文字,說明文字可以隨意編輯,右下方是一張固定的二維碼圖片,這三部分組成


image.png

2、在第一步的頁面上放一個(gè)按鈕“生成海報(bào)”,加一個(gè)點(diǎn)擊事件,點(diǎn)擊按鈕后跳轉(zhuǎn)到海報(bào)頁面

第一步渲染的頁面上,我需要把圖片地址和錄入的文字信息傳到海報(bào)的頁面,所以跳轉(zhuǎn)時(shí)帶了兩個(gè)參數(shù):一個(gè)地址,一個(gè)文字標(biāo)題

   saveImage() {
            //跳轉(zhuǎn)到海報(bào)頁面
            uni.navigateTo({
                url: '/pages/canTest/canTest?url=' + this.url + '&title=' + this.title
            })
        },

3、新的海報(bào)頁面,通過canvas把海報(bào)畫出來。

4、點(diǎn)擊新生成的海報(bào)鏈接,預(yù)覽海報(bào),再把海報(bào)保存到手機(jī)相冊(cè)

難點(diǎn):canvas不會(huì)用,不知道怎么畫。

二、操作過程:

1、海報(bào)頁面寫一個(gè)canvas組件

上面第2步點(diǎn)擊“生成海報(bào)”,跳轉(zhuǎn)到畫海報(bào)的頁面,頁面上寫一個(gè)canvas組件,然后給canvas一個(gè)點(diǎn)擊事件 ,點(diǎn)擊后可以預(yù)覽畫布畫的圖片。
style里面設(shè)置的是畫布的寬度和高度

<template>
<view class="demo">
    <canvas :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="myCanvas" id="myCanvas"
        @click="saveHB"></canvas>
</view>
</template>

2、獲取設(shè)備的信息和照片的信息,

(我的背景圖用的是網(wǎng)絡(luò)圖片:this.url)

2.1獲得屏幕的寬和高

 //獲取設(shè)備信息
  this.SystemInfo = await this.getSystemInfo();    

獲得了設(shè)備的寬和高就是獲得了畫布的寬和高,如果畫布高度不需要整個(gè)屏幕高,則可以減掉一部分,至于具體減掉多少自己調(diào)試就行,我減掉了150高度。

          this.canvasW = this.SystemInfo.windowWidth; // 畫布寬度
          this.canvasH = this.SystemInfo.windowHeight - 150; //畫布高度

2.2獲得背景圖片的實(shí)際寬和實(shí)際高

      this.goodsImg = await this.getImageInfo(this.url);   //獲取照片信信息

2.3計(jì)算背景圖片畫在畫布上時(shí)應(yīng)該畫的寬度和高度

這個(gè)在畫布上需要畫的背景圖片的寬度,我的設(shè)計(jì)稿左右padding :20rpx,所以實(shí)際畫的寬度是屏幕的寬度減掉兩邊一起20的寬度

         this.picWidth = this.canvasW - 20     

背景圖片的在畫布上的寬度確認(rèn)后,要保證原圖的縮放比,所以高度需要計(jì)算下:
原圖寬 / 原圖高=實(shí)際畫的寬 / 實(shí)際畫的高,
原圖的寬和高通過getImageInfo得到了,新需要畫的寬在上面一步得到了,所以實(shí)際需要的高為:實(shí)際畫的寬*原圖的高/原圖的寬

 this.picHight = this.picWidth * this.goodsImg.height / this.goodsImg.width

3.準(zhǔn)備二維碼圖片

我沒有自動(dòng)生成二維碼,直接準(zhǔn)備了一張二維碼圖片

   //二維碼的信息
        this.ewmImg = '/static/erweima.png';

4.畫背景圖

// 如果主圖,二維碼圖片,設(shè)備信息都獲取成功,開始繪制海報(bào),這里需要用setTimeout延時(shí)繪制,否則可能會(huì)出現(xiàn)圖片不顯示。

        //檢查系統(tǒng)信息,圖片信息都正常返回了
        if (this.goodsImg.errMsg == 'getImageInfo:ok' && this
            .SystemInfo.errMsg == 'getSystemInfo:ok') {
            // console.log('ok')
            //彈出提示,海報(bào)繪制中
            uni.showToast({
                icon: 'loading',
                mask: true,
                // duration: 5000,
                title: '海報(bào)繪制中',
            });
            setTimeout(() => {
                //準(zhǔn)備ctx畫布
                var ctx = uni.createCanvasContext('myCanvas', this);
                // 填充畫布的背景色為白色
                ctx.setFillStyle('#fff'); // 默認(rèn)白色
                //設(shè)置的畫布的高度與寬度和2.1章節(jié)的屏幕寬高保持一致
                ctx.fillRect(0, 0, this.canvasW, this.canvasH) // fillRect(x,y,寬度,高度)

   // 繪制主背景圖,因?yàn)橛?0rpx的padding,所以起始坐標(biāo)是10,10,
                //后面兩個(gè)參數(shù)是圖片需要畫的寬度和高度
                ctx.drawImage(this.goodsImg.path, 10, 10, this.picWidth, this.picHight)

5、繪制二維碼

image.png

//如果有設(shè)計(jì)圖,直接量設(shè)計(jì)圖即可,
//沒有設(shè)計(jì)圖就慢慢調(diào)試
//我的x坐標(biāo)是設(shè)備的寬度減掉前面文字組件的大概寬度:this.canvasW - 110
//我的Y坐標(biāo)是上面背景圖的高度加上中間隔離的高度:this.picHight + 45
//后面兩個(gè)參數(shù)就是需要畫的二維碼的寬和高: this.ewmW

ctx.drawImage(this.ewmImg, this.canvasW - 110, this.picHight + 45, this.ewmW, this.ewmW)

// 6、繪制文字標(biāo)題,多余文字自動(dòng)換行

                ctx.setFontSize(18); // setFontSize() 設(shè)置字體字號(hào)
                ctx.setFillStyle('#000000'); // setFillStyle() 設(shè)置字體顏色
                //canvas不能自動(dòng)換行,需要自行計(jì)算 ,直接copy過去用
                let _strlineW = 0;
                let _strLastIndex = 0; //每次開始截取的字符串的索引
                // let _strHeight = this.canvasW * rant + 30; //繪制字體距離canvas頂部的初始高度
                let _strHeight = this.picHight + 70;
                let _num = 1;
                for (let i = 0; i < this.title.length; i++) {
                    _strlineW += ctx.measureText(this.title[i]).width;
                    // if (_strlineW > this.canvasW * rant - 155) {
                    if (_strlineW > this.canvasW - 155) {
                        if (_num == 4 && 4) {
                            //文字換行數(shù)量大于4進(jìn)行省略號(hào)處理,想幾行就改下上面的數(shù)字
                            ctx.fillText(this.title.substring(_strLastIndex, i - 5) + '...', 10,
                                _strHeight);
                            _strlineW = 0;
                            _strLastIndex = i;
                            _num++;
                            break;
                        } else {
                            ctx.fillText(this.title.substring(_strLastIndex, i), 20, _strHeight);
                            _strlineW = 0;
                            _strHeight += 20;
                            _strLastIndex = i;
                            _num++;
                        }
                    } else if (i == this.title.length - 1) {
                        ctx.fillText(this.title.substring(_strLastIndex, i + 1), 20, _strHeight);
                        _strlineW = 0;
                    }
                }
                /* end */

以下內(nèi)容就比較容易了,直接照抄即可

                ctx.draw(true, (ret) => { // draw方法 把以上內(nèi)容畫到 canvas 中。
                    uni.showToast({
                        icon: 'success',
                        mask: true,
                        title: '繪制完成',
                    });
                    uni.canvasToTempFilePath({ // 保存canvas為圖片
                        canvasId: 'myCanvas',
                        quality: 1,
                        complete: (res) => {
                            this.hbUrl.push(res.tempFilePath)
                        },
                    })
                });
            }, 1000)
        } else {
            console.log('err')
        }

7.給canvas綁定一個(gè)點(diǎn)擊事件,點(diǎn)擊后,可以預(yù)覽圖片,預(yù)覽時(shí)可以長按圖片進(jìn)行轉(zhuǎn)發(fā)和保存了

//預(yù)覽圖片接口,urls參數(shù)為數(shù)組,上面繪圖得到的地址push到 this.hbUrl,就可以預(yù)覽了

saveHB() {
            // console.log('點(diǎn)擊了圖片')
            uni.previewImage({
                urls: this.hbUrl
            })
        },

下面時(shí)獲取圖片信息和獲取系統(tǒng)信息的方法

 // 獲取圖片信息
        getImageInfo(image) {
            return new Promise((req, rej) => {
                uni.getImageInfo({
                    src: image,
                    success: (res) => {
                        req(res)
                    },
                });
            })
        },

        // 獲取設(shè)備信息
        getSystemInfo() {
            return new Promise((req, rej) => {
                uni.getSystemInfo({
                    success: function(res) {
                        req(res)
                    }
                });
            })
        },

通過以上的方式,就可以生成海報(bào)圖片了,點(diǎn)擊海報(bào)也能預(yù)覽,在預(yù)覽頁面長按就能把圖片轉(zhuǎn)發(fā)或者保存到電腦了,后面的操作無需任何方法。

整體頁面的代碼如下,直接復(fù)制過去,稍微調(diào)試下就能使用。

    <template>
<view class="demo">
    <canvas :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="myCanvas" id="myCanvas"
        @click="saveHB"></canvas>
</view>
</template>
<script>
export default {
    components: {},
    data() {
        return {
            canvasW: 0, // 畫布寬
            canvasH: 0, // 畫布高
            picWidth: '', //圖片寬
            picHight: 260, //圖片高
            SystemInfo: {}, // 設(shè)備信息
            goodsImg: {}, // 主圖
            ewmImg: {}, // 二維碼圖片
            ewmW: 100, // 二維碼大小
            title: '營銷是做一切的事情,讓客戶來找我;銷售是做一切的事情,我去找客戶。',
            name: '周希奇', // 推薦人
            hbUrl: [], //儲(chǔ)存生成的圖片地址
        }
    },
    async onLoad(params) {
        //獲取從上衣頁面?zhèn)鬟^來的圖片參數(shù)
        this.title = params.title
        this.url = params.url
        // 獲取設(shè)備信息,獲取設(shè)備的寬高,畫布做一樣的高度
        this.SystemInfo = await this.getSystemInfo();
        console.log('this.SystemInfo', this.SystemInfo)
        this.canvasW = this.SystemInfo.windowWidth; // 畫布寬度
        this.canvasH = this.SystemInfo.windowHeight - 150; //畫布高度

        //獲取背景圖片的長與寬,按畫布的比例進(jìn)行縮放
        this.goodsImg = await this.getImageInfo(this.url);
        console.log('this.goodsImg', this.goodsImg)
        //圖片的寬度等于整個(gè)寬度減掉兩邊的padding
        this.picWidth = this.canvasW - 20
        //圖片的高度= this.picWidth * 實(shí)際的寬度/原來的高度
        this.picHight = this.picWidth * this.goodsImg.height / this.goodsImg.width
        console.log('畫的圖片寬度為:', this.picWidth)
        console.log('畫的圖片高度為:', this.picHight)

        //二維碼的信息
        this.ewmImg = '/static/erweima.png';

        // 如果主圖,二維碼圖片,設(shè)備信息都獲取成功,開始繪制海報(bào),這里需要用setTimeout延時(shí)繪制,否則可能會(huì)出現(xiàn)圖片不顯示。
        if (this.goodsImg.errMsg == 'getImageInfo:ok' && this
            .SystemInfo.errMsg == 'getSystemInfo:ok') {
            // console.log('ok')
            uni.showToast({
                icon: 'loading',
                mask: true,
                // duration: 5000,
                title: '海報(bào)繪制中',
            });
            setTimeout(() => {
                var ctx = uni.createCanvasContext('myCanvas', this);
                // 填充背景色,白色
                ctx.setFillStyle('#fff'); // 默認(rèn)白色
                ctx.fillRect(0, 0, this.canvasW, this.canvasH) // fillRect(x,y,寬度,高度)

                // 繪制商品主圖,二維碼
                ctx.drawImage(this.goodsImg.path, 10, 10, this.picWidth, this.picHight)
                ctx.drawImage(this.ewmImg, this.canvasW - 110, this.picHight + 45, this.ewmW, this.ewmW)
                // 3、繪制商品標(biāo)題,多余文字自動(dòng)換行
                ctx.setFontSize(18); // setFontSize() 設(shè)置字體字號(hào)
                ctx.setFillStyle('#000000'); // setFillStyle() 設(shè)置字體顏色
                /* str 這段代碼是我百度找的,參考別人的。canvas不能自動(dòng)換行,需要自行計(jì)算 */
                let _strlineW = 0;
                let _strLastIndex = 0; //每次開始截取的字符串的索引
                // let _strHeight = this.canvasW * rant + 30; //繪制字體距離canvas頂部的初始高度
                let _strHeight = this.picHight + 70;
                let _num = 1;
                for (let i = 0; i < this.title.length; i++) {
                    _strlineW += ctx.measureText(this.title[i]).width;
                    // if (_strlineW > this.canvasW * rant - 155) {
                    if (_strlineW > this.canvasW - 155) {
                        if (_num == 4 && 4) {
                            //文字換行數(shù)量大于二進(jìn)行省略號(hào)處理
                            ctx.fillText(this.title.substring(_strLastIndex, i - 5) + '...', 10,
                                _strHeight);
                            _strlineW = 0;
                            _strLastIndex = i;
                            _num++;
                            break;
                        } else {
                            ctx.fillText(this.title.substring(_strLastIndex, i), 20, _strHeight);
                            _strlineW = 0;
                            _strHeight += 20;
                            _strLastIndex = i;
                            _num++;
                        }
                    } else if (i == this.title.length - 1) {
                        ctx.fillText(this.title.substring(_strLastIndex, i + 1), 20, _strHeight);
                        _strlineW = 0;
                    }
                }
                /* end */

                ctx.draw(true, (ret) => { // draw方法 把以上內(nèi)容畫到 canvas 中。
                    uni.showToast({
                        icon: 'success',
                        mask: true,
                        title: '繪制完成',
                    });
                    uni.canvasToTempFilePath({ // 保存canvas為圖片
                        canvasId: 'myCanvas',
                        quality: 1,
                        complete: (res) => {
                            this.hbUrl.push(res.tempFilePath)
                        },
                    })
                });
            }, 1000)
        } else {
            console.log('err')
        }

        uni.showShareMenu({
            withShareTicket: true,
            menus: ["shareAppMessage", "shareTimeline"]
        })
    },
    methods: {
        saveHB() {
            // console.log('點(diǎn)擊了圖片')
            uni.previewImage({
                urls: this.hbUrl
            })
        },

        // 獲取圖片信息
        getImageInfo(image) {
            return new Promise((req, rej) => {
                uni.getImageInfo({
                    src: image,
                    success: (res) => {
                        req(res)
                    },
                });
            })
        },

        // 獲取設(shè)備信息
        getSystemInfo() {
            return new Promise((req, rej) => {
                uni.getSystemInfo({
                    success: function(res) {
                        req(res)
                    }
                });
            })
        },

        //保存相片到本地
        savePicture() {
            uni.saveImageToPhotosAlbum({
                filePath: this.hbUrl,
            })
        },
        //分享相片
        sharePicture() {
            uni.share({
                provider: 'weixin',
                imageUrl: this.hbUrl,
                type: 2,
                scene: 'WXSceneTimeline'
            })
        },

    },
}
</script>
<style lang="scss">
.button-container {
    display: flex;
    justify-content: center;
    position: fixed;
    top: 80%;
    height: 100rpx;
    width: 100%;

    button {
        width: 40%;
        background-color: #0055ff;
    }
}
     </style>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容