H5利用canvas實現海報功能


最近接到一個需求,微信中用戶上傳圖片生成海報。這個需求比較常規,實現思路也比較簡單,通過利用用戶的input輸入,對所上傳的圖片進行處理,最后通過第三方庫html2canvas合成對應的圖片即可。思路雖然簡單,但是在實現的過程中會遇到各種各樣的小問題,現在就將遇到的問題進行一次總結。

1、iphone以及部分android機型通過攝像頭拍攝的照片被旋轉了90

原因:由於目前的手機拍照基本都在2M以上,而ios中只要超過2M圖片就會自動旋轉。拍照后直接取出來的UIimage(用UIImagePickerControllerOriginalImage取出),它本身的imageOrientation屬性是3,即UIImageOrientationRight。如果這個圖片直接使用則沒事,但是如果對它進行裁剪、縮放等操作后,它的這個imageOrientation屬性會變成0。此時這張圖片用在別的地方就會發生旋轉。imageOrientation是只讀的,不能直接修改其值。

解決方法: 當拍照后,獲取input中的圖片數據,利用exif.jsExif.js 提供了 JavaScript 讀取圖像的原始數據的功能擴展,例如:拍照方向、相機設備型號、拍攝時間、ISO 感光度、GPS 地理位置等數據。)獲取到Orientation屬性,此屬性有四個值

1 表示旋轉0度,也就是沒有旋轉。
6 表示順時針旋轉90
8 表示逆時針旋轉90
3 旋轉180
我們要做的就是在拍照后,從input中獲取到圖片,然后得到它的Orientation值,根據Orientation值再對圖片進行進一步處理,處理代碼如下:

import EXIF from 'exif-js';
var file = {
    upload: function(e) {
        var file = e.target.files[0];
        var type = file.type.split('/')[0];
        if (type != 'image') {
            alert('請上傳圖片');
            return;
        }

        var size = Math.floor(file.size / 1024 / 1024);
        if (size > 3) {
            alert('圖片大小不得超過3M');
            return;
        };

        var reader = new FileReader();
        reader.readAsDataURL(file);

        var orientation = null;
        //獲取照片方向角屬性,用戶旋轉控制  
        EXIF.getData(file, function() {
            EXIF.getAllTags(this);
            orientation = EXIF.getTag(this, 'Orientation');

        });

        reader.onloadstart = function() {};
        reader.onloadend = function(e) {
            var dataURL = reader.result;
            var imaged = new Image();
            imaged.src = dataURL;
            imaged.onload = function() {
                var canvas = document.createElement('canvas');
                var ctx = canvas.getContext('2d');
                //普通環境下設置canvas的寬高
                var w = 0,
                    h = 0;
                if (this.width < 750) {
                    w = this.width;
                    h = this.height;
                    canvas.width = w;
                    canvas.height = h;
                } else {
                    w = 750;
                    canvas.width = w;
                    var scale = this.width / this.height;
                    w = w > this.width ? this.width : w;
                    h = w / scale;
                    canvas.height = h;
                }
                // if (navigator.userAgent.match(/iphone/i) || navigator.userAgent.match(/samsung/i)) {
                if (orientation != "") {
                    switch (orientation) {
                        case 3:
                            ctx.rotate(180 * Math.PI / 180);
                            ctx.drawImage(this, -w, -h, w, h);
                            break;
                        case 6:
                            //這里由於將圖片糾正了回來,所以也要重新設置canvas的高已達到高度自適應
                            canvas.width = 750;
                            var scale = this.height / this.width;
                            canvas.height = canvas.width / scale;
                            h = 750 > this.height ? this.height : 750;
                            w = h / scale;
                            ctx.rotate(90 * Math.PI / 180);
                            ctx.drawImage(this, 0, -h, w, h);
                            break;
                        case 8:
                            ctx.rotate(270 * Math.PI / 180);
                            ctx.drawImage(this, -h, 0, h, w);
                            break;
                        case 2:
                            ctx.translate(w, 0);
                            ctx.scale(-1, 1);
                            ctx.drawImage(this, 0, 0, w, h);
                            break;
                        case 4:
                            ctx.translate(w, 0);
                            ctx.scale(-1, 1);
                            ctx.rotate(180 * Math.PI / 180);
                            ctx.drawImage(this, -w, -h, w, h);
                            break;
                        case 5:
                            ctx.translate(w, 0);
                            ctx.scale(-1, 1);
                            ctx.rotate(90 * Math.PI / 180);
                            ctx.drawImage(this, 0, -w, h, w);
                            break;
                        case 7:
                            ctx.translate(w, 0);
                            ctx.scale(-1, 1);
                            ctx.rotate(270 * Math.PI / 180);
                            ctx.drawImage(this, -h, 0, h, w);
                            break;
                        default:
                            ctx.drawImage(this, 0, 0, w, h);
                    }
                } else {
                    ctx.drawImage(this, 0, 0, w, h);
                }
                //接下來對圖片進行操作
                

            };
        };
    },
    event: function() {
        $(".uploadfile").change(function(e) {
            file.upload(e);
        });

    },
    init: function() {
        this.event();

    }
};

module.exports = file; 

2、獲取微信用戶頭像生成海報無法顯示

 原因:  由於微信用戶的頭像是微信的域名,canvas由於安全機制的原因,限制了使用跨域的圖片,所以無法顯示用戶頭像的圖片。

解決方法:

1)后端在獲取用戶頭像的時候,將頭像圖片保存到服務器,生成該域名下新的圖片鏈接

2)使用nginx作為代理,將對應請求轉發到微信頭像圖片的服務器。這里展示第二種方法的實現

前端將圖片地址修改為對應的代理服務器域名地址

data.avatar.replace('thirdwx.qlogo.cn/', 'proxy.newmedium.xyz/wximg/')

nginx 代理配置

     server {
        listen       80;
        server_name  xxxxxx;
        add_header Access-Control-Allow-Origin *;
        location /wximg/ {
            proxy_pass http://thirdwx.qlogo.cn/;
        }
    }

 

由於項目靜態文件域名上了cdn,所以我們只能采用不同的域名作為代理域名,這里又涉及到跨域問題,還需要在服務器和前端處理一部,設置header Access-Control-Allow-Origin *,前端設置為允許跨域

    html2canvas(document.querySelector(".poster"), {
           backgroundColor: '#ffffff',
           useCORS: true
     })

三個步驟,一步都不能少。

3、頁面有聲音播放時調用html2canvas生成海報后,iphone手機會有重復聲音

  測試發現html2canvas在使用的時候如果頁面有音頻播放,在ios系統中,音頻會被復制一份,並且保持播放的狀態。

  剛開始一直找不到原因,最后沒有辦法,從源碼中找html2canvas的實現原理找到了問題所在。 html2canvas在將dom轉化為canvas的時候,會把頁面復制到一個iframe,然后在計算出對應元素標簽所在的位置進行處理。所以在復制頁面的時候,將音頻播放標簽aduio也復制到了iframe,隨后canvas完成后會刪除iframe標簽,但是復制到了iframe的音頻已經開始播放進程,無法跟隨iframe內容的清除而銷毀。

最后在html2canvas 的官方文檔中找到了解決方法。Html2canvas暴露接口ignoreElements,我們只要將aduio標簽置為忽略就行了。

html2canvas(document.querySelector(".poster"), {
    useCORS: true,
    ignoreElements: function(el) {
        if (el.tagName == 'AUDIO') {
            return true;
        }
   });

4、海報上傳數據太大

   canvas轉化為base64數據的時候,有時候用戶選擇文件圖片有點大,導致上傳性能差。最后查閱資料,可以通過toDataURL方法指定canvas 轉化為base64 圖片的質量來解決這個問題。

canvas.toDataURL("images/jpeg",0) ,第一個參數就是把圖片編碼為jpeg格式,第二個參數(0-1)就是指定圖片質量,數值越大質量越高


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM