【項目】前端圖片裁剪
把工作中做過的一些小東西或者功能總結(jié)記錄,分享學(xué)習
最近做了一個需求,是做視頻封面裁剪的,涉及到的一個功能點是自動裁剪,就是拿到一張圖片,自動裁剪圖片的中間區(qū)域成 一個正方形
其實這個挺簡單的,說到前端裁剪,無非就是使用 ,但是也避免不了會忘記其中的細節(jié),所以要寫文章記錄一下
我就挑按順序來詳細記錄一下
1、api 簡單介紹
2、api 詳細介紹
3、裁減中間區(qū)域
4、詳細代碼
1
api 簡單介紹
沒錯,用的就是.這個api完成我們的截圖功能,看起來好像沒有涉及到什么復(fù)雜的東西
但是實際上也的確沒有什么復(fù)雜的東西,只不過使用的時候會難以避免碰到一些坑而已
兼容性
.這個方法 的兼容性我們再來看一下,現(xiàn)在基本大部分瀏覽器已經(jīng)兼容了,就除了IE6-8吧
但是據(jù)我們公司統(tǒng)計,IE的使用人數(shù)都幾乎為0了
IE9 :0.0%
IE10:0.1%
IE11:0.1%
IE9都統(tǒng)計為0了,那以下的幾乎就可以忽略不計了,所以大膽放心使用
成功率
如果你可能還是會有些不放心,心里總是好像沒有什么底,不知道在別人電腦手機是否能正常展現(xiàn)
我們對這個功能也做了監(jiān)控,截圖的成功率高達99.5%!
所以我們大可以放心使用這個進行我們的前端截圖
2
api 詳細介紹
的參數(shù)還是挺多的,挺容易弄混的,所以這里必須要花大力氣寫清楚,反正每次用都是要看一次的
我也沒想著能夠一勞永逸
先來看下這個完整的 api(其實他的參數(shù)有很多個用法,這里只介紹我們這里截圖用到的)
里面涉及的是圖片的位置寬高,的位置寬高
這幾個參數(shù)如果不仔細想一下的話,是有一點弄混的第一個參數(shù),,就是Image生成的實例
后面四個參數(shù),表示的就是圖片的位置,寬高信息
以你的圖片為底圖,以imgX和imgY找到起始點,然后再以你想要的寬高裁出大小
比如這樣一張圖片,紅色區(qū)域就是我們裁剪出來的地方
緊接著,最后四個參數(shù),表示的就是畫布的位置,寬高信息
為什么需要這四個參數(shù)?
在上面四個圖片參數(shù)中,我們已經(jīng)裁減出了我們需要的圖片部分
我們要把圖片放到 上,所以我們需要確定我們要放在哪里啊!!
第一,要知道放置的起點,所以有了,
第二,要知道繪制的大小(用于縮放)
雖然我們已經(jīng)有了裁減出來的圖片大小,但是我們也要確定該圖片在上繪制多大
可以以此來完成縮放功能,如果你想原樣繪制在上面,那么你就大小設(shè)置成裁減的大小就ok
步驟就相當于是
先裁減圖片,然后再繪制到上
3
裁減中間區(qū)域
好了,上面我們介紹完了,就說這次我們的需求了,就是要裁減圖片中間區(qū)域因為我們在用戶上傳圖片做封面的時候,圖片是用戶上傳的五花八門的圖片
所以我們需要首先自動裁減成正方形做成封面,比如這樣
如果是寬>高,那么就高占滿
如果是高>寬,那么就寬占滿
所以我們需要獲取到以下這些數(shù)據(jù)
1、圖片的原始寬高
2、裁減的圖片位置
3、裁減的圖片大小
首先拿到圖片原始寬高,比較一下是更長還是更高照片視頻怎么裁剪,從而確定裁減的大小
高>寬,裁減的寬高=圖片的寬
寬>高,裁減的寬高=圖片的高
知道了裁減的寬高之后,就可以知道裁減的起始位置
像這樣
讓我們看一下大概的代碼
const image = new Image();
// 允許圖片跨域,不然cavnas輸出圖片會報錯
image.crossOrigin = "anonymous";
image.src = imgUrl;
image.onload = () => { ?
??const imgWidth = image.naturalWidth; ?
??const imgHeight = image.naturalHeight;
?let cutWidth = 0;
?let cutHeight = 0; ?
?if (imgWidth > imgHeight) {
? ?cutWidth = imgHeight;
? ?cutHeight = imgHeight;
?} else {
? ?cutWidth = imgWidth;
? ?cutHeight = imgWidth;
?} ?
?const y = (imgHeight - cutHeight) / 2;
?const x = (imgWidth - cutWidth) / 2; ?
const canvas = document.createElement("canvas");
canvas.width = cutWidth;
canvas.height = cutHeight; ?
const ctx = canvas.getContext("2d"); ?
?// 繪制的大小就是 裁減的圖片大小,
并且從左上角起始點開始繪制
?ctx.drawImage( ? ?
? ?image,
? ?x, y,
? ?cutWidth, cutHeight,
? ?0, 0,
? ?cutWidth, cutHeight
?); ?
?// 輸出圖片
?const dataUrl = canvas.toDataURL("image/png");
?console.log("裁減的結(jié)果", dataUrl);
};
碰到的問題
1、圖片跨域問題
當我們使用導(dǎo)出圖片的時候,如果圖片對象沒有設(shè)置可以跨域,那么就會報錯
如果在新建Image對象的時候,如果加上跨域?qū)傩?/p>
image.crossOrigin = "anonymous";
有時候設(shè)置了屬性還是一樣會報錯,可能是命中了緩存,所以我們最好還要在圖片訪問路徑加一個時間戳
img.src = `xxxx?time=${Date.now()}`;
4
詳細代碼
但是實際使用中,需要處理更多的問題,并且要封裝更加通用一些,我大概分了三個方法
,用來新建圖片示例
,根據(jù)圖片url和位置裁減出想要的區(qū)域
照片視頻怎么裁剪,根據(jù)圖片url和比例,裁減出中間區(qū)域
所以在這里我們只需要直接調(diào)用,傳入一個url就可以了,就會返回裁剪好的
function imgUpload(url) { ?
?const image = new Image();
?image.crossOrigin = "anonymous"; ?
?return new Promise((resolve, reject) => { ? ?
? ?const loaded = (event) => {
? ? ?image.onload = null;
? ? ?resolve( ? ? ? ?
? ? ? ? Object.assign(event, { ? ? ? ? ?
? ? ? ? naturalWidth: image.naturalWidth, ? ? ? ? ?
? ? ? ? naturalHeight: image.naturalHeight, ? ? ? ? ?
? ? ? ? imageObj: image,
? ? ? ?})
? ? ?);
? ?}; ? ?
? ?const errored = (event) => {
? ? ?image.onerror = null; ? ? ?
? ? ?console.log("img 加載error", event);
? ? ?reject(event);
? ?};
? ?image.onload = loaded;
? ?image.onerror = errored;
? ?image.onabort = errored;
? ?
? ?// 如果路徑是 base64 就不用加上時間戳,
? ? ? ? ? ?如果是http,防止跨域報錯所以加上 time
? ?image.src =
? ? ?url?.indexOf?.("data:image") > -1 ?
? ? ? ?url :
? ? ? ?`${url}?time=${Date.now()}`;
?});
}
async function getImageCutArea(url, position) { ?
?let imgInfo = await imgUpload(url); ?
?const { imageObj } = imgInfo; ?
?const { x, y, width, height } = position; ?
?const canvas = document.createElement("canvas");
?canvas.width = width;
?canvas.height = height; ?
?const ctx = canvas.getContext("2d");
?ctx.drawImage(
? ?imageObj,
? ?x, y, width, height,
? ?0, 0, width, height
?);
?try { ? ?
? ?const dataUrl = canvas.toDataURL("image/jpeg"); ? ?
? ?return Promise.resolve(dataUrl);
?} catch (e) { ? ?
? ?return Promise.reject(e);
?}
}
async function getImageCenterArea(url, aspect = 1) { ?
?const imgInfo = await imgUpload(url); ?
?const { naturalWidth, naturalHeight } = imgInfo; ?
?const imgHeight = naturalHeight; ?
?const imgWidth = naturalWidth; ?
?const imgRatio = imgWidth / imgHeight; ?
?const isImgMoreHigh = imgRatio < aspect; ?
?let width = 0; ?
?let height = 0; ?
?// 如果圖片更高,那么默認寬度100%占滿。
? ? ? ?否則寬度就是 以 aspect 為準占滿高度后的 適配寬度
?if (isImgMoreHigh) {
? ?width = imgWidth;
? ?height = imgWidth / aspect;
?} else if (!isImgMoreHigh) {
? ?height = imgHeight;
? ?width = imgHeight * aspect;
?} ?
?const y = (imgHeight - height) / 2; ?
?const x = (imgWidth - width) / 2; ?
?const result = await getImageCutArea(imgInfo, {
? ?x,
? ?y,
? ?width,
? ?height,
?});
?return result;
}
最后
鑒于本人能力有限,難免會有疏漏錯誤的地方,請大家多多包涵, 如果有任何描述不當?shù)牡胤剑瑲g迎后臺聯(lián)系本人,領(lǐng)取紅包
聲明:本站所有文章資源內(nèi)容,如無特殊說明或標注,均為采集網(wǎng)絡(luò)資源。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系本站刪除。