圖片粘貼上傳,大家基本都司空見慣了,不足為奇。(本文只是水記一下,有空深挖細(xì)耕)
某日產(chǎn)品大大發(fā)話:
- 某:“這個沖賬憑證上傳,能加個圖片粘貼上傳功能嗎?”
- 我(凡事一句):“為什么?”
- 某:“理由是:用戶覺得,截個屏 > 保存到本地 > 選擇這個文件上傳,太麻煩。直接截圖上傳,省事。”
- 我(無力反駁):“哦,我試試?!?/li>

由于用的 iView,先去扒扒有沒有 API 提供。文檔沒有,圍觀源碼,欸,居然寫了這么一段代碼,upload.vue#L7:
...
@paste="handlePaste"
...
...
handlePaste (e) {
if (this.paste) {
this.uploadFiles(e.clipboardData.files);
}
}
...
但是,為什么凡事都有個但是?人家還沒發(fā)布 releases...

這種類似的功能,網(wǎng)上搜羅也一大堆。哎呦喂,挺簡單的,監(jiān)聽 paste 事件,獲取 event 里面的內(nèi)容就行了。擼起袖子就是干,照著源碼畫葫蘆。
分分鐘就搞定了,圖片也能上傳。然鵝,為什么可粘貼區(qū)域這么詭異...

看來 iView 那個也是個半成品,我還是扶墻好了

開始去 MDN 看 paste ,結(jié)果寥寥幾字,沒什么參考性。
梅西表示,我現(xiàn)在慌的一匹...

順著摸到 W3 的 clipboard-event-paste:
...
The paste action has no effect in a non-editable context, but the paste event fires regardless.
...
表示并沒怎么看懂這段英文...
所以,大致意思是,不可編輯的元素,無效?加個屬性試試:
<div contenteditable="true" @paste="pasteHandler" ></div>
嗯,現(xiàn)在,只有點(diǎn)擊這個 div 才會觸發(fā)這個 paste 事件。
但是,但是,同時也帶來了用戶可以隨意輸入、編輯、粘貼任何內(nèi)容的問題......

我的內(nèi)心,時而堅強(qiáng),時而奔潰...

然后,我想了個搓的方式:
- 字體設(shè)置為 0
- 子元素 display: none
- 子元素字體大小為 0
.paste {
overflow: hidden;
height: 140px;
font-size: 0;
line-height: 140px;
text-align: center;
border: 1px dashed #dddee1;
background-color: #fff;
/deep/ * {
display: none !important;
font-size: 0 !important;
}
&:after {
display: inline-block;
font-size: 12px;
content: "點(diǎn)擊,粘貼圖片上傳";
}
}
這種方式,就是不顯示給你看,倒是非常不優(yōu)雅的解決了。
不過本身這種交互方式,就感覺怪怪的:
- 點(diǎn)擊一個區(qū)域
- Ctrl + V 粘貼圖片
- 上傳
暫且這樣吧,目前木有想到更好的方式。

待我發(fā)了個測試版后,發(fā)現(xiàn) FireFox 4.x 不支持。
經(jīng)測測試,paste 事件有響應(yīng)。但是 event 里面,files 為空,啊哈哈哈...
偶然發(fā)現(xiàn),其實,瀏覽器插入富文本到里面了:

再回想到 W3 的 clipboard-event-paste:
...
If the cursor is in an editable context, the paste action will insert clipboard data in the most suitable format (if any) supported for the given context.
...
再讀一遍 ...in the most suitable format ...

怎么辦呢?
- 獲取 img 元素
- 讀取 base64
- base64 轉(zhuǎn) Blob
- Blob 轉(zhuǎn) File
- 上傳
base64toBlob (base64Data) {
let byteString
if (base64Data.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(base64Data.split(',')[1])
} else {
byteString = unescape(base64Data.split(',')[1])
}
const mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0]
const ia = new Uint8Array(byteString.length)
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i)
}
return new Blob([ia], {type:mimeString})
},
hackForFireFox () {
// 等待 dom 更新
this.$nextTick(() => {
const $img = this.$refs.paste.querySelector('img')
if (!$img) {
return
}
const base64Data = $img.getAttribute('src')
const blob = this.base64toBlob(base64Data)
const file = new File([blob], 'image.png')
this.$refs.paste.innerHTML = '' // 清空上傳的圖片
this.$refs.upload.uploadFiles([file])
})
},
// 粘貼事件處理
pasteHandler (e) {
// 如果配置了運(yùn)行粘貼上傳
if (this.paste) {
// 高版本瀏覽器,能直接拿到 files
// 我司不考慮 IE,故未兼容處理
if (e.clipboardData.files.length > 0) {
this.$refs.upload.uploadFiles(e.clipboardData.files)
} else {
// 否則,嘗試讀取富文本
this.hackForFireFox()
}
}
}
總結(jié)
不是很好的解決方式,踩了幾個坑,算是個思路吧。后面有新想法,再補(bǔ)上。
其實,我的內(nèi)心,是不接受這種方式的...

—— 2018/07/31 By Vinci, Partly Cloudy.