歡迎關注富途web開發團隊,php , 前端需要你。缺人從眾
之前經常在微信中收到一些朋友分享帶頁面資訊的圖片(分享圖片),這種分享方式相對於尋常的結構化分享(連結分享)更容易於表現頁面內容。公司的產品童鞋也想在活動頁面中生成一張分享圖片(帶使用者專屬二維碼的),引導使用者分享傳播。
剛開始做這個功能時,我們是由後端根據使用者的資訊生成一張專屬的分享圖片,並將圖片上傳到圖片庫,獲得圖片連結;再由前端呼叫介面後獲取到分享圖片的連結,在頁面中用img
標籤展示這張圖片,配上一些引導的文案,提醒使用者“長按儲存分享”。但考慮到伺服器壓力、儲存等問題,我們只會對一個使用者做一次分享圖片生成的操作(當使用者再一次獲取分享圖片的時候,返回的是第一次生成的那張圖片),即整個活動期間,單個使用者生成的分享圖片的內容是不會更改的。這樣就使得一些動態變化的資料不能展示在分享圖片中,分享圖片中可展示的內容受到很大的限制。
後來,我們想能不能直接有前端生成這張分享快照呢?前端生成圖片,無需經過網路傳輸等操作,可減少服務的壓力,且可以每次都動態生成圖片,可以使分享圖片展示一些隨使用者操作而不斷變化的資料。之後發現已經存在一些html
生成canvas
或者img
的庫,所以我們就試了一下,看能不能直接由前端生成分享快照。
簡單思路
將html
轉換為image
,(或者dom
轉canvas
再轉image
),將生產的image
的連結(多數是base64)放到頁面的img
元素的src屬性上,再給出相關引導操作提示(如:長按儲存圖片),引導使用者分享/儲存圖片。
大家提起的html
轉canvas
或image
的庫主要有兩個:
demo試驗
簡單的例項
html程式碼中,主要包括了根據設計童鞋給的分享快照的原稿重構出的分享快照的html程式碼,和要放生成的圖片的img
容器。
<div class="actShareBox">
我是html!!
<!-- 要用於生成快照的html程式碼 -->
<div class="j_snapshot snapshot">
</div>
</div>
<div class="j_snapshot_img_box actShareImgBox">
我是dom-to-image生成的圖片
<!-- 用於展示dom-to-image生成的圖片 -->
<img src="" class="j_snapshot_img">
</div>
<div class="j_snapshot_img_box actShareImgBox">
我是html2canvas生成的圖片
<!-- 用於展示html2canvas生成的圖片 -->
<img src="" class="j_snapshot_img2">
</div>
複製程式碼
在js程式碼中,主要是呼叫兩個庫的API,生成圖片或者canvas。
// $snapshot為分享快照內容的dom元素,
// $snapshotImg為要放domtoimage生成的圖片的img元素
// $snapshotImg2為要放html2canvas生成的圖片的img元素
var $snapshot = document.querySelector('.j_snapshot');
var $snapshotImg = document.querySelector('.j_snapshot_img');
var $snapshotImg2 = document.querySelector('.j_snapshot_img2');
// domtoimage生成jpg的方法(這個庫還有其他的方法)
domtoimage.toJpeg($snapshot,{
quality: 1
}).then(function (dataUrl) {
// 直接生成了base64的url
$snapshotImg.src = dataUrl;
}).catch(function (error) {
console.error('oops, something went wrong!', error);
});
// html2canvas 生成canvas
html2canvas($snapshot,{
// useCORS: true, // 允許圖片跨域
backgroundColor: null,
logging: false,
}).then(function(canvas) {
// 'image/jpeg', 1.0
// 再利用canvas的toDataURL 方法,將canvas轉為圖片
var dataURL = canvas.toDataURL();
$snapshotImg2.src = dataURL;
}, function(err) {
console.error('oops, something went wrong!', err);
});
複製程式碼
上圖...
-
在android微信中生成的圖片(小米note3)
-
在ios微信中生成的圖片(iphone6p)
從上面六張圖中可以看出
-
從生成的圖片內容的還原度看,無論在ios中或android中,
html2canvas
都能將分享圖片html頁面中內容完整的生成出來,而dom-to-image
生成的圖片中,ios端生成的是一張殘缺的圖片(所有html頁面中圖片無法正常的展示),在android也同樣有部分圖片無法展示,且存在樣式錯亂問題。(這輪,html2canvas
勝) -
從清晰度看,細看對比中間的圖(
dom-to-image
生成的圖片)和右邊的圖(html2canvas
生成的圖片),能看出右圖中的文字、img圖片的清晰度都要比左圖的高很多(html中的背景圖片生成出來的清晰度,兩者都不是很好)(這輪,還是html2canvas
勝)
so~, 還是用html2canvas
吧。
遇到的問題
但使用html2canvas
生成分享圖片時,還是有遇到一些問題的,所以這裡將一些列出來。
剛開始用html/css重構分享快照時,我們有用到純文字、img
標籤圖片、css背景圖、對圖片/文字進行縮放。
第一次生成的圖片很不符合預期,生成的圖片與原圖效果相差較大。只能一一排查,尋找問題,解決問題了。
1.跨域圖片無法展示
我們的圖片資源是走cdn的,所以導致了圖片的連結域名與網頁的域名不一致,而產生了跨域問題,可能導致圖片無法展示。
解決:
步驟1:將html2canvas
中的引數設定中的useCORS
屬性改為true
,使html2canvas
接受處理跨域資源。
步驟2: 使圖片資源允許跨域(響應頭中加上 Access-Control-Allow-Origin: *
)
這樣就能顯示跨域圖片了~
第三方跨域圖片處理:
自己的圖片實現允許跨域還是比較容易的,但如果用到了第三方的不允許跨域的圖片(如微信頭像)呢,這時候需要我們再多做一下處理,將那張圖片變成允許跨域的啦....
看別人的做法是:在伺服器上實現第三方圖片的代理(將第三方圖片的域名變成自己的域名),再使自己的域名的資源允許跨域。
為了一個活動而修改伺服器的nginx
配置,是不是不太好(慫=.=)。
所以我們活動裡換了另一種方式。
後端不直接返回微信頭像,而是一個能對應相關微信頭像的連結(這個連結的資源允許跨域),當後端接收到這個連結的請求時,拉取微信頭像資訊,再返回給前端這個頭像的圖片資訊。[這裡的後端指的活動中的後端程式碼,與上面的改nginx
配置不是一個做法]
後來發現,這個拉取微信頭像的操作有時會超時,所以我們又做了層處理,在第一次獲取微信頭像後,將這個頭像上傳到自己的圖片庫中(非同步操作),以後訪問頁面,需要微信頭像時,就返回圖片庫中的圖片連結(當然,這個圖片庫連結也是可以跨域的)。
2.生成的分享圖片不清晰
這裡的不清晰指的有兩方面:
- 整張分享圖片的清晰度不夠
- 原分享頁面中的圖片在生成的分享圖片中的清晰度不夠
整張分享圖片清晰度不夠問題解決:
這裡的清晰度不夠的問題,是由於圖片的實際物理畫素點不夠的原因造成的,這裡我的處理方法修改html2canvas
的scale
屬性,增大繪製時的縮放比例,從而提高清晰度
html2canvas($snapshot, {
useCORS: true,
scale: window.devicePixelRatio*2 // 預設值是window.devicePixelRatio
backgroundColor: null,
logging: false
});
複製程式碼
原分享頁面中的圖片在生成圖片中清晰度不夠問題解決
我們在分享頁面中有用到兩種方式展示圖片:
- 使用
img
標籤元素引入圖片 - 使用元素中的背景圖片引入圖片
但從實際生成的圖片的效果發現,使用頁面中使用背景圖片的部分,在分享快照中會特別的不清晰,而且背景圖片的底部會有一些原頁面中沒有的小刺點(如下圖的背景圖裡,生成的圖片中富途logo下有一條類似的白色細線,但原頁面中卻沒有)。
而使用img
標籤元素引入圖片則不會存在這種情況。
-
縮放過的背景圖片
-
img元素的圖片
從圖中可以看出,兩張圖的頁面截圖(上部分圖)顯示的清晰度是差不多的,但通過html2canvs
生成圖片之後,明顯看出,背景圖的那張圖片的清晰度差好多,而且周圍還有些渣渣小點。
so: 如果分享快照中有用到圖片的話,還是儘量使用img
標籤,而不是用背景圖,以保證分享圖片的清晰度。
分享圖片頁面中雪碧圖
在重構分享圖片頁面時,裡面有個vs的內容,裡面的陣營圖片會根據使用者的陣營選擇和最終獲勝陣營的結果而展示不同的內容,而我們為了頁面的效能問題,還有開發的方便,將這些陣營圖片拼成了一張雪碧圖,通過class控制展示不同的圖片。
一開始我也沒想到怎麼用img
元素去展示雪碧圖,就還是用了背景圖來顯示陣營選擇這塊內容,後來就時不時被產品童鞋吐槽分享圖片中那部分內容太模糊了....。
後來想了想,如果使用的是雪碧圖的話,也是可以改成用img
標籤展示的:
<span class="emoji emoji01"><img src="/emoji.png" alt=""></span>
<style>
.emoji{
width: 10px;
height: 10px;
/*固定大小,超過的隱藏*/
overflow: hidden;
display: block;
position: relative;
}
.emoji.emoji01 img{
position: absolute;
/*使用top,left 偏移img圖片使之展示對應的位置*/
top: 0;
left: -20px;
}
</style>
複製程式碼
3.img
圖片對 transform
屬性的相容性不是很好
html2canvas
的官方文件中,有列了一些相容性不是很好的css屬性,所以自己多測試一下,不能支援的屬性,就換一種寫法啦~。
在活動中遇到的是,img
元素中的transform
的實現不太好。
例子中是想將圖片居中展示,所以給img
元素加了以下的css
:
img{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 100%;
}
複製程式碼
但實際上出來的效果是這樣的...。
總結
使用html2canvas
,將頁面元素轉為圖片的方式生成分享快照的方式是可行的,但在重構分享圖片頁面時,因儘可能使用常用的方式和css屬性,少用背景圖片,就能生成一張清晰度不錯的圖片啦~。
作者:Cynthia