巧用css的border屬性完成對圖片編輯功能的效能優化
一、需求場景:
最近閒來無事,boss提出了一個要求,研究相關程式碼並完成一個關於編輯圖片功能的效能優化,該功能的主要介面展示如下:
通過了幾分鐘的短暫試用,發現就是一個簡單的裁剪並儲存使用者選擇並上傳的圖片作為使用者頭像的功能。
主要功能點如下:
- 選擇圖片並上傳。
- 拖動中間選擇區域的位置或者大小選擇設定的圖片區域。
- 點選確定按鈕,完成圖片設定。
初步一看,貌似沒有什麼值得優化的地方,通過與boss深入瞭解後知曉,在選擇容量較大(超過10MB)的圖片時,瀏覽器響應速度會變得異常緩慢,具體表現是
拖動中間選擇區域的位置或者大小時,大約3-4秒才響應一次,嚴重影響了使用者的使用者體驗,作為一名專(zhuang)業(bi)的前端人員來說,是完全不能容忍的。
二、效能分析:
作為一名專(zhuang)業(bi)的前端人員來說,遇到問題的首要肯定是先進行效能分析。一般來說瀏覽器的效能問題無非就是CPU和記憶體消耗佔用。
通過對比檢視效能管理器中的chrome記憶體佔用後發現,選擇大容量圖片前,chrome的記憶體佔用是883MB,而選擇大容量圖片後,chrome的記憶體佔用達到了2232MB,
記憶體佔用暴漲將近了200%!這就是出現效能問題的所在。事不宜遲,開始著手研究並解決問題。
三、檢視程式碼:
通過檢視程式碼,發現實現該功能的程式猿(也可能是媛)是通過在背景渲染一張完整的image,前景的圖片選擇區域設定一個透明度為0.3的蒙版(圖中的黑色背景區域),
然後在選擇區域(圖中綠色方框)的背景區域再渲染一張image,再通過動態設定background-position的top、left和size來達到預覽並獲取選中區域圖片的目的。
html結構如下:
<div class="pic_area" style="display:block" ng-show="roomPhotoEdit.file"> <div class="pic_editor"> <!-- upload image --> <span class="pic" id="editing_pic">
<!-- 渲染的第一張圖片 -->
<img onload="angular.element(this).scope().roomPhotoEdit.initCrop()" ng-src="{{roomPhotoEdit.file}}" alt="">
</span>
<!-- 黑色蒙版背景區域 --> <span class="crop_dimmed"></span>
<!-- 圖中綠色方框區域 --> <span class="crop_box" ng-style="roomPhotoEdit.cropLayout" style="top:75px;left:75px">
<!-- 渲染的第二張圖片 --> <span class="drag_area" ng-style="roomPhotoEdit.edited"></span> <button class="drag_handle handle_lt" data-type="lt"></button> <button class="drag_handle handle_rt" data-type="rt"></button> <button class="drag_handle handle_lb" data-type="lb"></button> <button class="drag_handle handle_rb" data-type="rb"></button> </span> </div>
</div>
javascript程式碼如下(基於AngularJS):
scope.roomPhotoEdit.edited = { 'background-image': 'url(' + currentImage.attr('src') + ')', // 這裡多渲染了一張圖片 'background-position': backgroundPositionOption[imageSizeType], // 動態設定top和left 'background-size': backgroundSizeOption[imageSizeType] // 設定預覽區域的尺寸 }
通過程式碼可以很明顯的看到通過在class為drag_area的區域額外渲染一張背景圖片並動態修改background-position和background-size來達到預覽選擇區域圖片的目的。
由於額外渲染了一張圖片,如果使用者選擇的容量超大的圖片,則會導致瀏覽器記憶體佔用暴漲,如果能只渲染一張圖片,應該就能極大減少記憶體佔用,可以從此入手。
四、嘗試優化:
首先就是去除拖放區域中額外渲染的背景圖片。
可以從上圖中看到去除了背景圖片之後,拖放區域被黑色蒙版遮蓋住了,沒有預覽的效果了。
通過度娘之後得知可以通過動態設定黑色蒙版區域的border-width、border-style、border-color來達到顯示拖放區域的預覽圖片效果。
話不多說,先來直接看看效果。
再來看之前drag_area區域的html結構,可以發現,之前的背景圖片等樣式全部被去除了。
取而代之的之前的黑色蒙版區域crop_dimmed的樣式,可以看到設定了border-width、border-style和border-color,來達到顯示中間預覽區域的效果。
最後要做的只是,在預覽區域的拖放事件中動態去設定drop_dimmed區域的border-width屬性即可。
該屬性主要的計算規則如下(其中layoutTop為綠色方框區域的top屬性,layoutLeft為綠色方框區域的left屬性,containerWidth為外部容器的寬度,containerHeight為外部容器的高度,position.size為綠色方框區域的寬高,在這裡拖放區域只能等比拖放,所以是個正方形,寬高一致):
border-top-width: layoutTop + "px" border-right-width: containerWidth - layoutLeft - position.size + "px" border-bottom-width: containerHeight - layoutTop - position.size + "px" border-left-width: layoutLeft + "px"
五、驗收
程式碼改完了最後一步當然是驗收了,通過檢視修改後的記憶體佔用發現,即使選擇了大容量的圖片之後,記憶體佔用也穩定在了800MB左右,而且操作比之前流暢多了。
搞定,收工!
六、總結:
從最初的純粹為了圖片裁剪區域的預覽,而額外渲染了一張圖片開始,到最後的去除額外圖片渲染,而採用border-width的方案來達到裁剪區域預覽的目的。
前端er們做的事情無非為了使用者體驗這4個字不斷在努力。而前端的一切的方案和手段,最終目的都是為了提升使用者體驗。
畢竟,我們做出的是最接近使用者的產品。