以前惡搞了一張朋友的表情包,直接在百度上找了一個在線表情包製做器,忽然靈光一閃,要是支持攝像頭該多好,方便又快捷 (重點是省手機內存,不用拍照 :) ),二話不說,開始搬磚react
體驗地址git
圖片支持直接粘貼 和 拖拽
圖片和文字縮放,支持鼠標滾輪
支持圖片翻轉
支持捕捉攝像頭畫面當素材
使用的第三方庫github
antd
宇宙最強 ui 庫react-color
取色器react-draggle
拖拽dom-to-image
dom 節點轉成圖片頁面 使用 React
+ Antd
方便快捷,三下五除二就搞定了web
//每一行基本就是這樣子 const operationRow = ({ icon = "edit", label, component }) => ( <Row className={`${prefix}-item`}> <Col span={labelSpan} className={`${prefix}-item-label`}> <Button type="dashed" icon={icon}> {label} </Button> </Col> <Col span={valueSpan} offset={offsetSpan} className={`${prefix}-item-input`} > {component} </Col> </Row> ); 複製代碼
支持圖片拖拽npm
dragArea.addEventListener( "dragleave", e => { this.stopAll(e); this.removeDragAreaStyle(); }, false ); //移動 dragArea.addEventListener( "dragover", e => { this.stopAll(e); this.addDragAreaStyle(); }, false ); dragArea.addEventListener( "drop", e => { this.stopAll(e); this.removeDragAreaStyle(); const files = e.dataTransfer.files; this.renderImage(Array.from(files)[0]); }, false ); 複製代碼
支持 圖片 粘貼,這個也很簡單 綁定粘貼事件 拿到 event 裏面的 data 渲染出來就好了canvas
pasteHandler = e => { const { items, types } = e.clipboardData; if (!items) return; const item = items[0]; //只要一張圖片 const { kind, type } = item; //kind 種類 ,type 類型 if (kind.toLocaleLowerCase() != "file") { return message.error("錯誤的文件類型!"); } const file = item.getAsFile(); this.renderImage(file); }; //粘貼圖片 bindPasteListener = area => { area.addEventListener("paste", this.pasteHandler); }; 複製代碼
渲染圖片api
renderImage = file => { if (file && Object.is(typeof file, "object")) { let { type, name, size } = file; if (!isImage(type)) { return message.error("無效的圖片格式"); } this.setState({ loading: true }); const url = window.URL.createObjectURL(file); this.setState({ currentImg: { src: url, size: `${~~(size / 1024)}KB`, type }, scale: defaultScale, loading: false, loadingImgReady: true }); } }; 複製代碼
其餘就沒啥好說的了,常規的頁面佈局bash
生成圖片 本質上就是 利用 canvas 的 ctx.drawImage()
,markdown
繪製文字antd
const canvas = document.createElement('cavans') const ctx = canvas.getContext('2d') canvas.width = "預覽區域的寬" canvas.height = "預覽區域的高" //圖片 ctx.drawImage("URL",...attr) //文字 ctx.fillText(TEXT,...attr) //旋轉圖片 ctx.rotate(Math.PI/180 * 好多度) //縮放 也是 調用 drawImage, 改變 sy,sx 繪製的其實就實現縮放了 ctx.drawImage(URL,0,0,sx /scale,sy/scale,0,0,x,y) //轉成圖片 canvas.toDataURL('image/png',若是要壓縮這裏就填第二個參數) 複製代碼
懂原理了 其實不必一行一行這樣寫了 找到個 現成的 dom-to-image
的庫,肥腸的不錯,也是開源和組件化得魅力啊,利人利己
調用 api domToimage.toPng()
輕鬆搞定
drawMeme = () => { const { width, height, loadingImgReady,isCompress } = this.state; if (!loadingImgReady) return message.error("請選擇圖片!"); this.setState({ drawLoading: true }); const imageArea = document.querySelector(".preview-content"); const options = { width, height, } if(isCompress){ options.quality = defaultQuality } domToImage .toPng(imageArea, options) .then(dataUrl => { this.setState({ drawLoading: false }); Modal.confirm({ title: "生成成功", content: <img src={dataUrl} style={{ maxWidth: "100%" }} />, onOk: () => { message.success("下載成功!"); const filename = Date.now() const ext = isCompress ? 'jpeg' : 'png' var link = document.createElement("a"); link.download = `${filename}.${ext}`; link.href = dataUrl; link.click(); }, okText: "當即下載", cancelText: "再改一改" }); }) .catch(err => { message.error(err); this.setState({ drawLoading: false }); }); }; 複製代碼
要想拿到 MediaStream
, 調用 navigatar.mediaDevices()
便可, 若是想研究 webRTC
,這些 API 也是基礎, 我的不是很感興趣,就沒研究,暫時只用到這個藉口
navigator.mediaDevices .getUserMedia({ video: true, audio: true }) .then(stream => { const cameraUrl = window.URL.createObjectURL(stream); const hide = message.loading('盛世美顏即將出現...') //其餘代碼 this.setState( { cameraUrl, cameraVisible: true }, () => { setTimeout(()=>{ try { this.video.play(); } catch (err) { console.log(err); Modal.error({ title: "攝像頭失敗", content: err.message }); } finally{ hide() } },1000) } ); }) .catch((err)=>{ console.log(err) Modal.error({ title: "調用攝像頭失敗", content: err.toString() }); this.setState({ cameraVisible: false }); }); }) 複製代碼
這時頁面左上角 會彈出一個提示 問你是否是容許 使用攝像頭,贊成後 拿到 stream
,不然進入 cath
使用 URL.createObjectURL()
拿到一個臨時的 url 連接
而後將 連接 設置成 <video src={臨時 URL}/>
調用 video.play()
//jsx <video style={{ display:"block", margin:"0 auto" }} ref={video => (this.video = video)} src={cameraUrl} width={previewContentStyle.width} height={previewContentStyle.height} /> 複製代碼
這時就會看見一個 帥氣的臉龐 出如今了屏幕上 !
這時來到了最後一步,截取畫面,也很簡單 把 video 節點畫在 canvas 上, 而後 toDataURL()
蹬蹬,搞定
screenShotCamera = () => { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const { width, height } = previewContentStyle; canvas.width = width; canvas.height = height; ctx.drawImage(this.video, 0, 0, width, height); const data = canvas.toDataURL("image/png"); message.success('截取攝像頭畫面成功!') this.setState({ currentImg: { src: data }, cameraVisible:false, scale: defaultScale, loading: false, loadingImgReady: true }); }; 複製代碼
下載圖片 基於 HTML5
的 download
屬性很好實現
const filename = Date.now() const ext = isCompress ? 'jpeg' : 'png' const link = document.createElement("a"); link.download = `${filename}.${ext}`; link.href = dataUrl; link.click(); 複製代碼
而後默認觸發一次點擊事件 搞定
這樣一個支持 攝像頭
的 表情包製做器就完成了, 這時真的體會到了 npm
強大生態 和 組件化的 好處, 也學習到了 各類新 api 的使用! 老鐵沒毛病