摘要:寫在前面微信小程序出來已經(jīng)有一段時間了,上也有很多人開源了很多項目。但是由于微信平臺的限制底層能力調(diào)用為一系列封裝,圖表的制作一直是個比較頭疼的問題。這個圖形庫已經(jīng)完成基本的圖形建設(shè),已經(jīng)支持瀏覽器,,以及微信小程序等環(huán)境。
寫在前面
微信小程序出來已經(jīng)有一段時間了,github上也有很多人開源了很多項目。但是由于微信平臺的限制(底層Canvas能力調(diào)用為一系列JSBridge封裝),圖表的制作一直是個比較頭疼的問題。當(dāng)前的成熟圖表庫無法正常運行,即使經(jīng)過改造,性能也有很大問題。
當(dāng)前已經(jīng)有做的比較好的圖表庫,如xiaolin3303的 wx-charts,但是只是為小程序量身定做的,在普通的H5頁面就無法執(zhí)行。因此,在開發(fā)中萌生了寫一個多環(huán)境下兼容的圖形庫的念頭。
這個圖形庫已經(jīng)完成基本的圖形建設(shè),已經(jīng)支持PC瀏覽器,WAP,React以及微信小程序等環(huán)境。并上傳至github,大家可以自由使用:wx-chart,并歡迎在Issues中提出BUG以及修改建議。
效果圖無圖無真相,首先上個效果圖吧
工程結(jié)構(gòu)wx-chart 圖形庫參考了眾多圖形庫的構(gòu)建,分為三層,如下圖所示:
最下方一層為兼容層,其中最主要的是 WxCanvas 和 WxCanvasRenderingContext2D
用于兼容瀏覽器以及微信小程序的環(huán)境。在本章節(jié)中,一會兒會展開講解實現(xiàn)原理。
中間層為基礎(chǔ)組件以及動畫庫,最上層為實現(xiàn)的各種圖形。這兩部分會在后續(xù)的章節(jié)中再另行闡述。
首先,我們實現(xiàn)這個中間層的目的很明確,就是完成一套與W3C Canvas一致的接口層,忽略小程序的底層接口,并使得上層調(diào)用時與普通Canvas使用方式一致。
小程序的架構(gòu)是類似于React Native的JSBridge混合開發(fā)模式,因此其Canvas繪圖實現(xiàn)也很有局限性,與普通的瀏覽器對比,主要有四個不同點:
節(jié)點和Canvas對象創(chuàng)建:小程序的Canvas節(jié)點(嚴格意義上小程序沒有節(jié)點的概念)id的標識屬性為canvas-id,其創(chuàng)建JS對象使用wx.createCanvasContext方法
屬性賦值不同:在W3C的規(guī)范中,繪制的屬性都是賦值形式的,例如:
ctx.fillStyle = "#ffffff"; ctx.font = "40px Arial";
而小程序全部是調(diào)用形式的:
ctx.setFillStyle("#ffffff"); ctx.setFontSize(20);
每次繪圖后需要調(diào)用 draw 方法完成渲染
有些Canvas的能力與原生的參數(shù)有區(qū)別,如shadowBlur,shadowOffsetX均要調(diào)用wx.setShadow;有些能力并沒有實現(xiàn),例如lineDash屬性,clip剪切能力等。
其中,以上這些基本也都是由于其JSBridge的實現(xiàn)機理導(dǎo)致的,有興趣的同學(xué)可以Google一下具體了解。
那么,問題來了,我們的中間層能夠解決上面那些問題?實現(xiàn)思路是什么?
第一個問題很簡單,我們聲明節(jié)點同時書寫id與canvas-id,而其他三個問題我的解決思路是:
能夠檢測小程序環(huán)境
與W3C標準一樣,實現(xiàn)Canvas & CanvasRenderingContext2D 兩個類:我們稱其為WxCanvas & WxCanvasRenderingContext2D
使用Object.defineProperty將調(diào)用轉(zhuǎn)換為賦值
當(dāng)調(diào)用有區(qū)別和沒有實現(xiàn)的函數(shù)時,自動處理。
檢測小程序環(huán)境export function checkWX() { return __GLOBAL__DEBUG__WX__ === true || (typeof wx != "undefined" && typeof wx === "object"); }
忽略__GLOBAL__DEBUG__WX__,此變量用于調(diào)試。我們簡單檢測 wx 全局是否存在。代碼
實現(xiàn)兼容類WxCanvas主要邏輯如下:
class WxCanvas { constructor(id, config, contextOptions) { let me = this; // 檢查小程序環(huán)境 me.isWeiXinAPP = checkWX(); me._config = extend({}, WX_CANVAS_DEFAULT_PROPERTY, me.initConfig(config)); // 提前獲取Canvas與context(這里忽略了webgl的情況) let {canvas, context} = this.acquireContext(id, config); me._canvas = canvas; me._ctx = context; // 實例化WxCanvasRenderingContext2D me.wxCanvasRenderingContext2D = new WxCanvasRenderingContext2D(canvas, context, contextOptions); return me; } /** * Create Canvas context * @param {String} id * @param {Object} config * @returns {*} */ acquireContext(id, config) { let me = this, canvas, context; // Outer canvas config let handlerCanvas = config.canvas; if (me.isWeiXinAPP) { if (is.String(id)) { canvas = context = wx.createCanvasContext(id); } else { //... } } else { if (handlerCanvas) canvas = handlerCanvas; else canvas = is.String(id) ? document.getElementById(id) : (typeof HTMLCanvasElement != "undefined" && id instanceof HTMLCanvasElement) ? id: null; if (typeof canvas != "undefined") { context = canvas.getContext && canvas.getContext("2d"); } } // ... return {canvas, context}; } getContext(str){ } set height() {...} get height() {...} set width() {...} get width() {...} }
WxCanvas的實現(xiàn)還是比較簡單的,主要注意的就是acquireContext方法,在這里我們判斷了小程序與非小程序環(huán)境,并獲得Canvas上下文。
下面我們看下WxCanvasRenderingContext2D的實現(xiàn)。
class WxCanvasRenderingContext2D { constructor(canvas, context, options) { let me = this; me.canvas = canvas; me._ctx = context; me.isWeiXinAPP = checkWX(); // 聲明canvas屬性緩存棧,用于優(yōu)化性能 me._ctxOptions = options; me._propertyCache = [extend({}, WX_CANVAS_CTX_DEFAULT_PROPERTY, options)]; me.cp = me._propertyCache[0]; // 屬性賦值轉(zhuǎn)化 me.createStyleProperty(); me.createShadowsProperty(); // 后續(xù)省略,我們就拿前兩個舉例足以... return me; } createStyleProperty() { let me = this; let styleProperty = ["fillStyle", "strokeStyle"]; styleProperty.forEach(p => { Object.defineProperty(me, p, { get: () => { return me.cp[p]; }, set: (value) => { if (value) { return me._wxSetPropertyCallable(value.toLowerCase(), p) } } }) }); } _wxSetPropertyCallable(value, propertyName, wxSetName = "set" + propertyName.replace(/(w)/, v => v.toUpperCase())) { let me = this; if (is.Null(value) || is.Undefined(value)) { return value; } // performance if (me.cp[propertyName] === value) { return value; } if (me.isWeiXinAPP) { me._ctx[wxSetName](value); me.cp[propertyName] = value; } else { me._ctx[propertyName] = value; me.cp[propertyName] = me._ctx[propertyName]; } return value; } ... draw(ctu = true) { if (this.isWeiXinAPP) { this._ctx.draw(ctu); } } }
上面的例子,我們只舉例 fillStyle, strokeStyle 兩個賦值屬性的兼容轉(zhuǎn)換??梢钥吹?,我們定義了通用方法_wxSetPropertyCallable,在小程序環(huán)境下,它會調(diào)用set[wxSetName](也就是setFillStyle和setStrokeStyle),而在普通環(huán)境下會正常的屬性賦值。
再來看我們的draw方法,同樣只在微信小程序中有效,但是遺憾的是每次繪制以后還是要調(diào)用draw方法一次,這是唯一的與W3C方法有區(qū)別的事情。不過,這主要是為了性能考慮,因為JSBridge每一次的調(diào)用都是有成本的,如果每次設(shè)置我們都自動的調(diào)用draw會大幅增加這個成本。
以上完成以后,我們就可以做到類似于W3C標準,在小程序中繪圖了:
import WxCanvas, { WxCanvasRenderingContext2D } from "wx-chart/util/wxCanvas" let wxCanvas = new WxCanvas("myCanvas", {width: "800px", height: "500px"}); let wxCanvasContext = wxCanvas.getContext("2d"); wxCanvasContext.fillStyle="#FF0000"; wxCanvasContext.fillRect(0,0,80,100); wxCanvasContext.draw();一些遇到的問題
在實現(xiàn)兼容庫時,有幾個比較棘手的問題。
首先是小程序并沒有實現(xiàn) measureText 方法,因此我們需要模擬一個:
measureText(text, fontSize = this.fontSize) { let me = this; if (me.isWeiXinAPP) { if (!text) { return 0; } let textLen = text.length; let hanzi = text.match(new RegExp(REG_HANZI, "g")); let hanziNum = !!hanzi ? hanzi.length: 0; let otherNum = textLen - hanziNum; return {"width": fontSize * (otherNum + hanziNum * 2) / 2 + fontSize / 4}; } else {//...} }
我們對漢字和非漢字進行了處理。不過,需要承認的是,即便是這樣,也與實際有一些不準。
另外,textBaseline,textAlign 也需要特殊處理(當(dāng)前1.1版本已經(jīng)支持)
結(jié)束由于時間和和篇幅有限,第一段文章就先寫到這里。有錯誤或者不完善的情況請多多諒解和支持。也換用大家使用wx-chart 圖形庫并提出意見和建議。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/88916.html
摘要:前端日報精選第期寫給前端應(yīng)屆生的職業(yè)規(guī)劃建議應(yīng)用編譯優(yōu)化之路進階篇命名空間模式解析源碼解析之任務(wù)管理入門教程快速上手聊聊改變歷史中文正式發(fā)布,帶來種新的圖表類型關(guān)系圖解好好寫代碼吧使用手冊掘金發(fā)布在即將全面支持掘金仿懂球帝 2017-10-10 前端日報 精選 【第1074期】寫給前端應(yīng)屆生的職業(yè)規(guī)劃建議webpack 應(yīng)用編譯優(yōu)化之路JS進階篇--命名空間模式解析gulp源碼解析之任...
摘要:微信小程序中提供了的支持,利用自行繪制圖表。關(guān)注我的項目查看完整代碼。 微信小程序中圖表現(xiàn)狀 由于微信小程序本身框架的限制,很難集成目前已有的圖表工具,顯示圖表目前有兩種方案:1、服務(wù)器端渲染圖表,輸出圖片,微信小程序中直接顯示渲染好的圖片,比如highcharts提供了服務(wù)端渲染的能力hightcharts server render,這種方式需要后臺有一套渲染服務(wù),并且有一定的網(wǎng)絡(luò)...
摘要:本期大綱確定縱坐標的范圍并繪制根據(jù)真實數(shù)據(jù)繪制折線相關(guān)閱讀在微信小程序中繪制圖表在微信小程序中繪制圖表關(guān)注我的項目查看完整代碼。相關(guān)閱讀在微信小程序中繪制圖表在微信小程序中繪制圖表 本期大綱 1、確定縱坐標的范圍并繪制 2、根據(jù)真實數(shù)據(jù)繪制折線 相關(guān)閱讀:在微信小程序中繪制圖表(part1)在微信小程序中繪制圖表(part3) 關(guān)注我的 github 項目 查看完整代碼。 確定縱坐...
閱讀 1368·2019-08-30 15:44
閱讀 2113·2019-08-30 11:04
閱讀 529·2019-08-29 15:17
閱讀 2552·2019-08-26 12:12
閱讀 3139·2019-08-23 18:09
閱讀 931·2019-08-23 15:37
閱讀 1530·2019-08-23 14:43
閱讀 2933·2019-08-23 13:13