此次主要記錄有關前端截圖和拼圖等處理。css
原因:接了一個活動需求,要求頁面打開以後,能夠長按觸發保存圖片,而且圖片下方須要帶上圖和二維碼的內容,以方便圖片分享到朋友圈後能夠長按識別二維碼打開頁面。(有時候純分享頁面連接到朋友圈,好友未必會點進去,圖片的分享方式比點連接高一些吧~)html
接到需求後前端
toDataURL
能夠獲取到圖片的dataUrl 雖然html2canvas好用,可是它仍是有一些須要注意到的地方,開發過程當中遇到的主要是兩個問題:a. 涉及的圖片跨域 b. 不支持漸變色字的截圖nginx
先說一下html2canvas的基本使用:它接受一個Element參數和Options對象參數,前者指的是將要截圖的DOM,後者則是截圖的相關配置.能夠看這裏>>git
目前html2canvas方法返回的是一個promise(在前期在線預研的時候,用cdn引了一個比較舊的版本,那時的html2canvas還不夠成熟,不會promise,致使用的時候報then is not a function)github
實際使用的代碼以下,很簡單。canvas
const contentDom: HTMLElement | null = document.querySelector('.draw-target'); if (contentDom) { html2canvas(contentDom, { x: 0, y: 0, // 支持跨域訪問 useCORS: true, // 移除不須要匯入的元素,越少越好 ignoreElements: element => { // 這裏指定了忽略.no-pic的元素 if (element.className.indexOf('no-pic') !== -1) { return true; } return false; } }) .then(canvas => { canvas.getContext('2d').imageSmoothingEnabled = false; resolve(canvas.toDataURL('image/png')); }) .catch(error => { console.log('生成報錯', error); reject(error); }); } else { reject('error'); }
代碼很簡單,可是頁面涉及到圖片資源跨域的時候,就得配合服務端解決圖片跨域的問題了,這裏要注意:其餘域圖片雖然能被頁面展現出來,但不表明它能被腳本跨域訪問。因此考慮截圖需求的時候必定要判斷好是否有跨域的圖片資源,要讓圖片那邊接受js的跨域調用,一般這個只須要nginx上配一下就行了。跨域
首先已知,咱們經過上面提到的截圖,拿到了圖片的data url,而後手裏還有一個要拼接上的圖片,能夠用canvas和它的drawImage搞定。promise
已知,手裏有兩個圖片的地址,實際上,咱們要作的只是建立img而且給img.src給填上圖片地址,在img.onload那裏把img交給canvas的drawIamge去繪圖便可,一樣這裏繪圖也是考慮跨域問題的,不過個人需求裏兩個圖片,第一個自己就是一個data url能夠說沒跨域了,或者說前面已經解決了,第二個能夠考慮就和腳本放一塊兒,那就沒有跨域問題啦。瀏覽器
而後拼圖要考慮到,圖片之間尺寸大小不一,在個人需求裏:A圖(頁面截圖) 和 B圖(純圖片),最終拼成一張C圖,通常C圖的寬度就按A圖的寬度來用便可,也就是A圖爲基礎圖,B圖適應A圖的寬度來從新調整繪入時的長寬(widthFinalB = widthA base; heightFinalB = widthFinalB / widthB heightB) 這裏簡單起見,B圖在設計稿上base爲1.
先把img賦值部分封裝成一個promise
const getImg2Draw = (src): Promise<ImageObj> => { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { resolve({ img, width: img.naturalWidth, height: img.naturalHeight }) }; img.onerror = (err) => { reject(err) } img.src = src; }) }
這樣最後promise會給我返回圖片以及它的實際長寬,方便計算。
而後分別canvas將A圖和B圖畫上canvas
// 獲取A圖 getContentImg().then(base64 => { // 獲取截圖部分 Promise.all([getImg2Draw(base64), // 獲取二維碼部分 getImg2Draw('./public/extra.png')]) .then((imgs) => { const [mainImg, extraImg] = imgs const $canvas = document.getElementById('canvas') as HTMLCanvasElement // 獲取截圖寬度,截圖是基於html的因此也是實際頁面寬度 const mainWidth = mainImg.width; // 獲取截圖的高度 const mainHeight = mainImg.height; // B的原始寬度 const extraWidth = extraImg.width; // B的原始高度 const extraHeight = extraImg.height; // 設置canvas的寬高 $canvas.width = mainWidth // 計算出基於A圖寬的比例B圖最終高度 const appendHeight = mainWidth / extraWidth * extraHeight // 計算總體高度 const allHeight = mainHeight + appendHeight $canvas.height = allHeight // 清空舊內容 canvas.clearRect(0, 0, mainWidth, allHeight) // 先把A圖懟上去 canvas.drawImage(mainImg.img, 0, 0, mainWidth, mainHeight) // 再把B圖部分懟上去 canvas.drawImage(extraImg.img, 0, mainHeight, mainWidth, appendHeight) // 獲得最終繪圖 const img = new Image(); img.src = $canvas.toDataURL('image/png'); document.body.appendChild(img); }).catch(err => { console.error(err) })
這個時候遇到另一個問題:拼圖的背景色,因爲我這邊拼圖是兩塊圖直接拼在一塊兒,若是各自有各自的背景色卻是沒什麼關係,拼圖的時候各自保留各自背景色便可,但我此次用的是統一背景色,並且仍是漸變的背景色。若是兩張圖各自掌管各自顏色,很容易出現拼接時有一條線,除非畫圖的時候把兩邊接觸面的背景色計算好,以便無縫銜接。
這裏直接由最終繪圖的canvas來自行畫背景色,兩張圖處理時都不要本身的背景色,也就是先畫布畫好背景,而後再兩張圖往畫布貼上便可。
漸變背景的畫法:
// 背景色用漸變色本身畫 const gradientBg = canvas.createLinearGradient(0, 0, 0, allHeight) gradientBg.addColorStop(0, '#5D79A7') gradientBg.addColorStop(1, '#304D7C') canvas.fillStyle = gradientBg canvas.fillRect(0, 0, mainWidth, allHeight)
把這段畫背景色放在繪AB圖以前便可,可是可能html2canvas在使用的時候要加一下參數,以及頁面的背景色要處理一下。
backgroundColor: null
這塊實際上簡單化,在頁面渲染完後生成圖片覆蓋在頁面上方,用絕對定位處理,並透明展現。
img.style.cssText = 'width:100%;height:100%;position:absolute;top:0;left:0;right:0;bottom:0;opacity:0;z-index:9;';
綜上,基本完成了截圖+拼圖的實現,其實回想起來實現上很簡單,就是實現過程要解決各類各樣的問題,不過方法總比問題多~
其實期間還遇到過一些坑,以及還沒解決但最後和需求可有可無的問題:
最後補充一下文中沒說起的參考資料:
canvas漸變
關於drawImage