HT for Web獨創的向量圖片設計架構,使其具有強大豐富的動態圖形呈現能力,但從最近知乎熱議的“Adobe Photoshop 是否已經過時?”的話題,大家能體會到很多情況下實際專案不可能完全採用向量,專案中還是有大量的傳統非向量的靜態圖片,畢竟工具強大且現有資源豐富,但為了使靜態圖片呈現出更多的動態效果,HT for Web提供了較多渲染方式,例如對HT for Web的圖元設定‘body.color’的style後圖元即可達到自動染色的效果,這裡將對這種染色技術進行講解。
上圖呈現了本文例子最終的呈現效果,中間為原始圖片是http://hightopo.com/images/banner.png ,對其分別設定了紅、黃和藍三種顏色後的染色效果,完全基於HT for Web實現這麼個簡單拓撲的話程式碼不過十多行,不過今天我們不借助HT for Web提供的‘body.color’的染色功能,我們將採用最原始的方式生產染色圖片。
dataModel = new ht.DataModel(); graphView = new ht.graph.GraphView(dataModel); graphView.addToDOM(); var root = new ht.Node(); root.setImage('ht-for-web.png'); dataModel.add(root); ['blue', 'yellow', 'red'].forEach(function(color){ var node = new ht.Node(); node.setImage('ht-for-web.png'); node.s({ 'body.color': color }); dataModel.add(node); var edge = new ht.Edge(root, node); edge.s({'edge.color': color}); dataModel.add(edge); });
染色的原理就是對圖片畫素內容的操作,可以理解為一個 convertColor(r, c, rawColor, …)->finalColor 的畫素函式轉換, 其中的r和c分別代表畫素所在行和列,rawColor為原始顏色資訊,…代表其他的引數資訊,例如我們進行染血的顏色資訊,最終通過convertColor處理返回的finalColor為轉換後的畫素值。
圖片染色操作可以說是最簡單的影象處理了,本質就是rawColor * tintColor,這裡的tintColor可以理解為HT for Web的body.color值,一般該資訊代表監控系統的告警級別狀態等資訊,通過染色讓使用者更直觀的觀察到裝置執行告警狀態。這裡的rawColor * tintColor相乘是本質是分別對兩個顏色的rgb進行相乘,一般情況下最後的alpha的透明度,會保持原始rawColor的值不進行操作處理。
toRGBA = (function(){ var map = {}, // string - [128,128,128,255] canvas; return function(color){ var g, data = map[color]; if (!data) { if(!canvas){ canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; } g = canvas.getContext('2d'); g.clearRect(0, 0, 1, 1); g.fillStyle = color; g.beginPath(); g.rect(0, 0, 1, 1); g.fill(); data = g.getImageData(0, 0, 1, 1).data; data = map[color] = [data[0], data[1], data[2], data[3]]; } return data; }; })(); function init(){ var image = new Image(); image.src = 'banner.png';// 'ht-for-web.png'; image.onload = function(){ dataModel = new ht.DataModel(); graphView = new ht.graph.GraphView(dataModel); graphView.addToDOM(); var root = new ht.Node(); root.setImage(image); dataModel.add(root); ['blue', 'yellow', 'red'].forEach(function(color){ var node = new ht.Node(); node.setImage(createColorImage1(image, color)); dataModel.add(node); var edge = new ht.Edge(root, node); edge.s({ 'edge.color': color }); dataModel.add(edge); }); }; } function createColorImage1(image, color) { var width = image.width; var height = image.height; var canvas = document.createElement('canvas'); var context = canvas.getContext( "2d" ); canvas.width = width; canvas.height = height; context.drawImage(image, 0, 0, width, height); var rgba = toRGBA(color); var r = rgba[0]/255, g = rgba[1]/255, b = rgba[2]/255; var pixelData = context.getImageData(0, 0, width, height); var pixels = pixelData.data; for (var i = 0; i < pixels.length; i += 4) { pixels[i+0] *= r; pixels[i+1] *= g; pixels[i+2] *= b; } context.putImageData(pixelData, 0, 0); return canvas; }
以上程式碼為實現的全部邏輯,有兩點需要注意,顏色值通過getImageData返回的是0~255的值,進行染色相乘我們需要基於0~1的格式,因此需要先除以255轉換後再相乘。另外一點為了方便使用者開發,顏色值允許是任意html支援的顏色格式,有可能輸入red,rgba(255,125,128,0.1)等格式,以上toRGBA程式碼在閉包中返回了個可根據輸入格式返回顏色陣列的工具函式,其藉助的也是在一個畫素的canvas記憶體中動態繪製,再通過getImageData來決定顏色資訊,再通過map快取計算結果,這種方式比列舉一堆顏色對映表來得精簡。
當然採用這種染色演算法在圖元是純黑色的時候是有問題的,因為黑色的rgb值都為0,因此無論乘以什麼顏色結果也都為0,實際應用場景可以要求美工避免做純黑色的圖片,或者程式碼對純黑色的圖片進行特殊的運算處理。
以上方式僅是第一種染色方式,因此為createColorImage1,下篇將介紹第二種染色方式createColorImage2。