最近項目用到了canvas轉圖片,可是因爲canvas對文字排版的支持很是弱,通常咱們在canvas上畫不一樣排版的文字(好比豎排文字)都是利用js計算橫縱座標,而後一個字一個字地畫出來,今天無心中看到一個使用svg的方法,記錄下來,供之後開發時參考,相信對其餘人也有用。html
參考資料:ios
SVG
svg和canvas都支持圖形渲染,它們各有側重:canvas
foreignObject元素是個很是強大的元素,它能夠在其中使用具備其它XML命名空間的XML元素,簡單來講,就是咱們能夠利用foreignObject元素把html「畫在」svg裏面!緩存
示例以下。其中xmlns表示XML命名空間,標記它是一個svg或者一個xhtml。注意:svg和xhtml都屬於XML。app
<svg xmlns="http://www.w3.org/2000/svg"> <foreignObject width="120" height="50"> <body xmlns="http://www.w3.org/1999/xhtml"> <p>文字。</p> </body> </foreignObject> </svg>
另外,全部的svg都必須加上這個svg的命名空間標識,不然它就會以XML文檔樹的形式渲染。示例以下:ide
//渲染出來的是數字 <svg width="12" height="12" viewBox="0 0 12 12"><path d="M10.263 10.874L6 6.61l-4.264 4.264a.431.431 0 0 1-.61-.61L5.39 6.001 1.128 1.736a.431.431 0 0 1 .61-.61L6 5.391l4.264-4.263a.431.431 0 0 1 .61.61L6.61 6l4.262 4.264a.43.43 0 1 1-.61.609z" stroke="#979797" fill="#999"/></svg> //渲染出來的是圖形 <svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="M10.263 10.874L6 6.61l-4.264 4.264a.431.431 0 0 1-.61-.61L5.39 6.001 1.128 1.736a.431.431 0 0 1 .61-.61L6 5.391l4.264-4.263a.431.431 0 0 1 .61.61L6.61 6l4.262 4.264a.43.43 0 1 1-.61.609z" stroke="#979797" fill="#999"/></svg>
因爲咱們能夠利用foreignObject把html轉化爲SVG,而後咱們能夠把svg畫在canvas裏面,最後用canvas把它轉化爲圖形。svg
示例以下:wordpress
//首先初始化canvas var canvas = document.querySelector('#canvas'); var context = canvas.getContext('2d'); //獲取文字的長度 context.font = "20px 微軟雅黑"; var textLength = context.measureText(text).width; //畫一個svg圖片 var img = new Image(); //對文字長度作判斷,一行的文字字要大些,二行的文字字要小些 if(textLength <= 135) { img.src = 'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="270" height="45"><body xmlns="http://www.w3.org/1999/xhtml" style="margin:0;color:#e7793f;text-align:center;font-size:40px;line-height:45px;font:微軟雅黑;">'+ text +'</body></foreignObject></svg>'; } else { img.src = 'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="270" height="60"><body xmlns="http://www.w3.org/1999/xhtml" style="margin:0;color:#e7793f;text-align:left;font-size:25px;line-height:30px;font:微軟雅黑;overflow: hidden;display: -webkit-box;text-overflow: ellipsis;--webkit-box-orient: vertical;--webkit-line-clamp: 2;word-break: break-all;">'+ text +'</body></foreignObject></svg>'; } //把圖片繪製上去,並利用canvas生成圖片 img.onload = function() { context.drawImage(img, 72, 1080); document.querySelector('#ticket').src = canvas.toDataURL('image/png'); }
理論上有三種方法來解決:
1.利用js逐個字符逐行繪製,可是這樣對英文要作特殊處理,不能單個字符繪製,而應該旋轉90度。相關參考代碼以下:code
fillTextVertical: function(context, text, x, y) { var canvas = context.canvas; var arrText = text.split(''); var arrWidth = arrText.map(function (letter) { return context.measureText(letter).width; }); var align = context.textAlign; var baseline = context.textBaseline; //emoji的位置 var emoji_arr = fn.findEmoji(text); //emoji第一位的緩存 var emoji_first = ''; if (align == 'left') { x = x + Math.max.apply(null, arrWidth) / 2; } else if (align == 'right') { x = x - Math.max.apply(null, arrWidth) / 2; } if (baseline == 'bottom' || baseline == 'alphabetic' || baseline == 'ideographic') { y = y - arrWidth[0] / 2; } else if (baseline == 'top' || baseline == 'hanging') { y = y + arrWidth[0] / 2; } context.textAlign = 'center'; context.textBaseline = 'middle'; // 開始逐字繪製 arrText.forEach(function (letter, index) { // 肯定下一個字符的縱座標位置 var letterWidth = arrWidth[index]; // 是否須要旋轉判斷 var code = letter.charCodeAt(0); //是不是emoji的第二位 if(emoji_arr.indexOf(index) != -1) { emoji_first = letter; return; } //是不是emoji if(emoji_arr.indexOf(index - 1) != -1) { letter = emoji_first + letter; emoji_first = ''; } else { if (code <= 256) { context.translate(x, y); // 英文字符,旋轉90° context.rotate(90 * Math.PI / 180); context.translate(-x, -y); } else if (index > 0 && text.charCodeAt(index - 1) < 256) { // y修正 y = y + arrWidth[index - 1] / 2; } } context.fillText(letter, x, y); // 旋轉座標系還原成初始態 context.setTransform(1, 0, 0, 1, 0, 0); // 肯定下一個字符的縱座標位置 var letterWidth = arrWidth[index]; y = y + letterWidth; }); // 水平垂直對齊方式還原 context.textAlign = align; context.textBaseline = baseline; },
上面方法的缺點是,對emoji符號使用正則匹配的,但仍然會有部分emoji符號匹配不到致使亂碼。
2.使用foreignObject繪製。可是有一些bug,就是在安卓機上emoji表情會錯位,並且在ios上會被莫名截斷。
3.使用html2canvas庫。仍是有一個bug,就是對英文的豎排支持不是很理想,英文會橫排顯示。
總之上面的方法各有各的優缺點,完美的豎排兼容emoji的實現是不可能的,這輩子都不可能的!0.0