摘要:將預覽的圖片上傳,后端生成,在管理系統中下載。技術要點文字排版設置指定背景顏色引入外部字體繪制文字圖片將生成的圖片轉成上傳這里根據后端協商,此處后端要求將圖片生成,并點擊批量下載實現步驟文字排版在一般容器中,如果要實現文字的排版很容易。
最近遇到一個業務需求,在小程序端定制預覽功能,并在預覽的圖片中使用指定的外部字體。將預覽的圖片上傳OSS,后端生成PDF,在管理系統中下載。
但是…………,經過實踐發現,小程序盡管做了分包處理,依舊不能在本地存放字體包,把字體放OSS上返回,但是出現跨域,盡管配置了允許跨域,依舊不行。而且!!!小程序的canvas API沒法設置字體,沒有h5中canvas中的context.font = "字體名稱"方法。最終決定曲線救國,放棄小程序端的預覽生成canvas功能,將canvas引入字體,生成圖片等操作放在管理系統中,采用原生canvas來實現。
canvas文字排版
canvas設置指定背景顏色
canvas引入外部字體
canvas繪制文字圖片
將canvas生成的base64圖片轉成file上傳(這里根據后端協商,此處后端要求)
將圖片生成PDF,并點擊批量下載
實現步驟 canvas文字排版 在一般HTML容器中,如果要實現文字的排版很容易。比如:實現文本超出自動換行,默認文本超出容器寬度就會自動換行,也可以使用word-wrap:break-word實現強制換行。
實現文字豎排,有幾種方式:
給文本容器設置writing-mode樣式:(存在兼容性問題)
writing-mode:vertical-rl;//垂直方向自右而左的書寫方式。即 top-bottom-right-left 或者 writing-mode:vertical-lr;//垂直方向內內容從上到下,水平方向從左到右
具體效果如圖:
但是這個對于瀏覽器也存在一定兼容性問題,使用的時候需要注意。
使用寬度控制換行:(不存在兼容性,推薦方式)
設置每行的寬度為一個字大小,利用文本超出默認換行的特性,或者設置超出強制換行,實現文本豎排。
利用br標簽實現或者每個文字存放一個標簽實現換行:(很死板的寫法,比較low,不推薦)
給每個文字后添加br標簽,或者每個文字放一個標簽,這樣寫靈活性不高,非常不推薦!
在canvas中實現文字排版實現文字橫排
canvas中,如果文本超出canvas大小,并不會自動換行,會直接在超出的后面繼續繪制成一排。
canvas中也沒有直接可以設置換行的api,那該怎么實現換行呢?
可以通過js控制,通過計算當前繪制文字的x坐標,如果x坐標大于canvas的寬度,將x坐標賦值為0(繪制的起始點x坐標),y坐標累加一個文字的高度,從而實現文本換行。
實現文字豎排
豎排的邏輯和橫排是一樣的。文字豎排只是y坐標累加,趟超過canvas的高度時,將y坐標賦值為0(繪制的起始點y坐標),x坐標累加一個文字的高度,從而實現豎排且文本換行。
部分代碼片段
/** * canvas繪制文字 * @param {CanvasRenderingContext2D對象} context * @param {繪制內容} text * @param {起始點x坐標制} x * @param {起始點y坐標制} y */ drawTextVertical(context, text, x, y) { let startX = x, startY = y; //記錄開始的位置,用于文字換行賦值 let spaceCount = 0; let arrText = text.trim().split(""); let formatText = text.replace(///g, "").split(""); // 去掉單斜杠 let align = context.textAlign; let baseline = context.textBaseline; context.textAlign = "center"; context.textBaseline = "middle"; context.font = "Pacifico" // 開始逐字繪制 arrText.forEach(function (letter, index) { // 確定下一個字符的縱坐標位置 // 是否需要旋轉判斷 let code = letter.charCodeAt(0); // 計算文字間距 let letterWidth = 22 * 2.3; if (code <= 256) { context.translate(x, y); // 英文字符,旋轉90° context.rotate(90 * Math.PI / 180); context.translate(-x, -y); } if (code !== 47) context.fillText(letter, x, y); // 旋轉坐標系還原成初始態 context.setTransform(1, 0, 0, 1, 0, 0); // 單斜杠換行或者長度超過8 此處要過濾在第9字是換行的符號的情況 if ((code === 47 && !spaceCount) || (!spaceCount && index && index % 7 === 0)) { // 單斜杠/ 代表換行 charCode=47 spaceCount += 1; y = startY; x = index ? (startX + letterWidth) : x; startX = x; } else if (code !== 47) { // 如果是空格 減少字間距 if (code !== 32) { y = y + letterWidth; } else { y = y + letterWidth / 2 } } }); // 水平垂直對齊方式還原 context.textAlign = align; context.textBaseline = baseline; }canvas設置背景顏色
canvas生成圖片的時候可以指定圖片格式(jpg,jpeg,png等),但是只能生成位圖(放大會失真)。如果想提高canvas生成圖片的質量,可以引入 hidpi-canvas-polyfill 插件,具體使用可以參考這篇文章 解決canvas生成圖片模糊 。
canvas生成圖片的背景默認是透明的,如果想多帶帶設置背景顏色,可以使用ctx.fillStyle進行填充,但是設置文字顏色,則文字顏色會覆蓋背景顏色,因為設置文字顏色也是使用ctx.fillStyle。那么,這種情況可以使用以下辦法解決:
1.使用canvas.getImageData復制畫布上的像素數據
2.循環遍歷復制的每個像素點,然后給每個像素設置rgb值
3.將設置好的流數據通過putImageData放回畫布上。
let imageData = ctx.getImageData(0, 0, width, height); for (let i = 0; i < imageData.data.length; i += 4) { // 當該像素是透明的,則設置成白色 if (imageData.data[i + 3] == 0) { imageData.data[i] = 255; imageData.data[i + 1] = 255; imageData.data[i + 2] = 255; imageData.data[i + 3] = 255; } } ctx.putImageData(imageData, 0, 0);
但是要注意背景和文字的繪制順序,必須先繪制背景,再繪制文字,如果順序顛倒,則文字會出現很明顯的鋸齒狀,有點模糊,這就和定位中z-index原理類似。
canvas引入外部字體1.首先引入字體庫,為了節省本地空間,可以從服務端引入,但是需要注意跨域問題,
也可以將字體庫放本地,直接相對路徑引入。
// 從服務端引入 @font-face { font-family: "FZCUJINLJW"; src: url("https://www.xxxx.com/FZCUJINLJW.TTF") ; } // 本地引入 @font-face { font-family: "FZCUJINLJW"; src: url("../../assets/FZCUJINLJW.TTF") ; }
2.通過CanvasRenderingContext2D對象設置字體,字號等
ctx.font = "24px FZCUJINLJW"; ctx.fillStyle = "#db9a00";//填充顏色canvas繪制文字圖片
let canvas = document.getElementById("canvas"); let ctx = canvas.getContext("2d");//拿到一個CanvasRenderingContext2D對象 ctx.beginPath();// 開始繪制文字 ctx.font = `${FONT_SIZE}px FZCUJINLJW`; ctx.fillStyle = "#db9a00";//填充顏色 ctx.fillText("繪制的內容", /*繪制的x坐標*/, /*繪制的y坐標*/); let imgBase64 = canvas.toDataURL("image/png", 1); ctx.closePath(); ctx.save();// 保存當前畫布內容 //如果需要在畫布上循環繪制多次,需要手動清除畫布上已經保存的內容,如果不清除,則畫布內容會疊加。 ctx.clearRect(0, 0, canvasObj.width, canvasObj.height);canvas生成圖片并上傳服務端
通過ctx.toDataURL可以獲取到畫布內容的base64編碼
let imgBase64 = canvas.toDataURL("image/png", 1);
如果服務端支持使用base64上傳,則不用處理,此處因為后端需要file文件類型,所以需要將base64轉成file對象,代碼如下:
let file = dataURLtoFile(imgBase64, "jpg"); // 將base轉為file對象 function dataURLtoFile(urlData, fileName) { var bytes = window.atob(urlData.split(",")[1]); //去掉url的頭,并轉換為byte var mime = urlData.split(",")[0].match(/:(.*?);/)[1]; //處理異常,將ascii碼小于0的轉換為大于0 var ab = new ArrayBuffer(bytes.length); var ia = new Uint8Array(ab); for (var i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } return new File([ab], fileName, { type: mime }); }
轉成file對象后,通過FormData格式上傳
let formdata = new FormData(); formdata.append("multipartList", file); ajax.post(url,data:formdata).then()
此處需要注意,當canvas生成的圖片比較小時(比如5kb以下),有可能導致文件上傳失敗,我之前踩過此坑。
此處是和后端商量,將canvas生成的圖片上傳服務端,并返回圖片的OSS地址,再將此地址作為參數傳給后端,獲取到PDF的下載鏈接,前端通過window.open(url)的方式實現文件下載。
let uploadUrl = window.interfercesPrefix + "/admin/goods/tbgoods/uploadImages"; let downLoadUrl = "/app/goods/tbgoods/downLoadPdf"; // 上傳圖片 ajaxUploderImg({ url: uploadUrl, data: formdata }).then(res => { // 將圖片作為參數獲取PDF下載地址 this.props.dispatch(downLoadPdf({ url: downLoadUrl, imgUrl: res.data })); }).catch(err => { if (err) { notification["error"]({ message: err.message, description: "圖片繪制出錯,請重試!", }); } else { notification["error"]({ message: "下載出錯,請返回" }); } })
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105169.html
摘要:最近碰到個需求,需要把當前頁面生成,并下載。但這并不是真的截圖,而是通過遍歷頁面結構,收集所有元素信息及相應樣式,渲染出。由于只能將它能處理的生成,因此渲染出來的結果并不是與原來一致。 最近碰到個需求,需要把當前頁面生成pdf,并下載。弄了幾天,自己整理整理,記錄下來,我覺得應該會有人需要 :) 項目源碼地址:https://github.com/linwalker/... html2...
摘要:最近碰到個需求,需要把當前頁面生成,并下載。但這并不是真的截圖,而是通過遍歷頁面結構,收集所有元素信息及相應樣式,渲染出。由于只能將它能處理的生成,因此渲染出來的結果并不是與原來一致。 最近碰到個需求,需要把當前頁面生成pdf,并下載。弄了幾天,自己整理整理,記錄下來,我覺得應該會有人需要 :) 項目源碼地址:https://github.com/linwalker/... html2...
摘要:接下來,亮出自己做的簡歷。登錄進入后,就可以選擇一個你喜歡的簡歷模板進行制作簡歷了。將頁面左邊的工具欄拿掉,然后將簡歷寬度放大到接近瀏覽器寬度即可達到像素最高的效果。此外,會自動保存你做的簡歷,方便下次編輯。 以下文章摘自我的博客,原文鏈接 下面的簡歷圖片不上傳了,想看的點擊原文鏈接就能看到了。 簡述下原理:首先找一個可以在線制作簡歷并提供簡歷模板的網站,然后在模板上填好自己的信息,并...
閱讀 2604·2021-11-02 14:39
閱讀 4321·2021-10-11 10:58
閱讀 1446·2021-09-06 15:12
閱讀 1837·2021-09-01 10:49
閱讀 1326·2019-08-29 18:31
閱讀 1882·2019-08-29 16:10
閱讀 3331·2019-08-28 18:21
閱讀 866·2019-08-26 10:42