做這個嘗試,只為了解決一個問題:如何在網頁中讀取剪貼簿中的圖片資料,並在頁面中展示或儲存到圖片伺服器。場景可以簡單描述為,通過任意螢幕截圖工具(QQ、旺旺,PrintScreen鍵等),截圖之後網頁中的文字框內貼上(Ctrl+V、右鍵等),並在頁面中使用圖片資料。文字描述比較抽象,我們用一個gif圖片來形象的展示一下效果:
ClipboardEvent(剪貼簿事件)
ClipboardEvent 介面描述了與修改剪下板相關的事件,這些事件包括 cut 、copy 和 paste 事件。
先解決一個問題:為什麼要說ClipboardEvent?
首先,無論是截圖還是在網頁中“複製圖片”,最終都是將圖片資料儲存到了系統剪貼簿中。其次,當執行“貼上”操作的時候是將剪貼簿中的資料讀出來使用,而讀取剪貼簿中的圖片(檔案)資料,更需要直接使用paste
事件完成。
第二個問題:為什麼要在文字框中“貼上”,是必須的嗎?
先從paste
事件來說,經測試paste
事件可以繫結到任意HTML元素上的,並非必須使用文字框。再來猜測下使用場景,貼上操作涉及到資料寫入問題,內容不可能寫入到頁面中的只讀區域,所以按常理來說,只可能是貼上到頁面中的可寫區域。再者,最容易讓人想到的場景便是一個聊天會話,因此首先想到的便是文字框控制元件。
功能實現
功能邏輯比較簡單,截圖完成或在網頁中複製圖片後,圖片資料就儲存到系統剪貼簿上了。我們程式碼中需要實現的就是在paste
事件中實現剪貼簿中的影象資料讀取和處理邏輯。拿到影象資料後,就可以根據自己的業務邏輯進行下一步操作了。程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
(function() { var sender = document.getElementById('J_MsgSender'), list = document.getElementById('J_MsgList'); function pasteImage(imgObj) { var file = imgObj.getAsFile ? imgObj.getAsFile() : imgObj, reader = new FileReader(); // 讀取檔案後將其顯示在網頁中 reader.onload = function(e) { var img = new Image(), p = document.createElement('p'); img.src = e.target.result; p.appendChild(img); list.appendChild(p); }; } sender.addEventListener('paste', function(ev) { // 通過事件物件訪問系統剪貼簿 var ev = ev || window.event, clipboardData = ev.clipboardData, i = 0, items, item, files; if (clipboardData) { items = clipboardData.items; files = clipboardData.files; if (files && files.length) { pasteImage(files[0]); return; } if (!items) { return; } for (; i < items.length; i++) { if (items[i].kind === 'file' && items[i].type.match(/^image\//i)) { item = items[i]; break; } } // 如果存在圖片資料 if (item) { pasteImage(item); // 讀取該圖片 } } }); })(); |
程式碼很簡單,但有些細節需要注意。在MDN的paste事件說明文件中有說明,資料是存放在事件物件的clipboardData
屬性中的。而在對clipboardData(DataTransfer型別)的說明中,檔案型別的資料應該是被存放在files屬性中。這一點在使用拖拽上傳等情況時確實是這樣的,但是在對截圖和複製的網頁圖片的處理上,各瀏覽器的實現似乎出現了差異。
在Chrome瀏覽器中,從剪貼簿讀取檔案時,files屬性中並沒有內容,而在items屬性中卻可以找到。而在Firefox中,files屬性中存在圖片資料,而在items中卻找不到檔案型別的資料。因此,我不得不在上示程式碼中做了存在性判斷,增加了額外的邏輯。
此外,IE瀏覽器對剪貼簿的處理比較特殊,它將剪貼簿資料放在了window作用域下,通過window.clipboardData
對剪貼簿資料進行操作。但在IE Edge中測試,並未對截圖資料進行成功讀取,暫不做描述。
其他
如上程式碼所示,當clipboardData物件中的files屬性中沒有資料時,我們可以通過遍歷items中的元素,並通過元素的kind
和type
屬性判斷元素型別,最後通過getAsFile
方法將元素轉換為檔案型別。
其實除了getAsFile
方法,還有一個類似的方法,可以將元素轉換成字串,用法示例如下:
1 2 3 |
item.getAsString(function(str) { console.log(str); }); |
該方法可以根據複製源的不同,轉換出包含文字格式(比如:字型、字號等)的字串,如圖:
對於文字型別的處理,相信是非常有用的一個方法。
結束語
目前,剪貼簿事件還是試驗中的功能,在後續的標準文件中可能被修改。我們在實際使用中應該做好相容處理,附相容性參考、線上演示。