亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

開發(fā)適用于微信小程序的跨平臺(tái)圖表庫(kù):part1

mingde / 1808人閱讀

摘要:寫在前面微信小程序出來已經(jīng)有一段時(shí)間了,上也有很多人開源了很多項(xiàng)目。但是由于微信平臺(tái)的限制底層能力調(diào)用為一系列封裝,圖表的制作一直是個(gè)比較頭疼的問題。這個(gè)圖形庫(kù)已經(jīng)完成基本的圖形建設(shè),已經(jīng)支持瀏覽器,,以及微信小程序等環(huán)境。

寫在前面

微信小程序出來已經(jīng)有一段時(shí)間了,github上也有很多人開源了很多項(xiàng)目。但是由于微信平臺(tái)的限制(底層Canvas能力調(diào)用為一系列JSBridge封裝),圖表的制作一直是個(gè)比較頭疼的問題。當(dāng)前的成熟圖表庫(kù)無法正常運(yùn)行,即使經(jīng)過改造,性能也有很大問題。

當(dāng)前已經(jīng)有做的比較好的圖表庫(kù),如xiaolin3303的 wx-charts,但是只是為小程序量身定做的,在普通的H5頁(yè)面就無法執(zhí)行。因此,在開發(fā)中萌生了寫一個(gè)多環(huán)境下兼容的圖形庫(kù)的念頭。

這個(gè)圖形庫(kù)已經(jīng)完成基本的圖形建設(shè),已經(jīng)支持PC瀏覽器,WAP,React以及微信小程序等環(huán)境。并上傳至github,大家可以自由使用:wx-chart,并歡迎在Issues中提出BUG以及修改建議。

效果圖

無圖無真相,首先上個(gè)效果圖吧

工程結(jié)構(gòu)

wx-chart 圖形庫(kù)參考了眾多圖形庫(kù)的構(gòu)建,分為三層,如下圖所示:

最下方一層為兼容層,其中最主要的是 WxCanvas 和 WxCanvasRenderingContext2D
用于兼容瀏覽器以及微信小程序的環(huán)境。在本章節(jié)中,一會(huì)兒會(huì)展開講解實(shí)現(xiàn)原理。
中間層為基礎(chǔ)組件以及動(dòng)畫庫(kù),最上層為實(shí)現(xiàn)的各種圖形。這兩部分會(huì)在后續(xù)的章節(jié)中再另行闡述。

WxCanvas & WxCanvasRenderingContext2D

首先,我們實(shí)現(xiàn)這個(gè)中間層的目的很明確,就是完成一套與W3C Canvas一致的接口層,忽略小程序的底層接口,并使得上層調(diào)用時(shí)與普通Canvas使用方式一致。

小程序的架構(gòu)是類似于React Native的JSBridge混合開發(fā)模式,因此其Canvas繪圖實(shí)現(xiàn)也很有局限性,與普通的瀏覽器對(duì)比,主要有四個(gè)不同點(diǎn):

節(jié)點(diǎn)和Canvas對(duì)象創(chuàng)建:小程序的Canvas節(jié)點(diǎn)(嚴(yán)格意義上小程序沒有節(jié)點(diǎn)的概念)id的標(biāo)識(shí)屬性為canvas-id,其創(chuàng)建JS對(duì)象使用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;有些能力并沒有實(shí)現(xiàn),例如lineDash屬性,clip剪切能力等。

其中,以上這些基本也都是由于其JSBridge的實(shí)現(xiàn)機(jī)理導(dǎo)致的,有興趣的同學(xué)可以Google一下具體了解。

那么,問題來了,我們的中間層能夠解決上面那些問題?實(shí)現(xiàn)思路是什么?
第一個(gè)問題很簡(jiǎn)單,我們聲明節(jié)點(diǎn)同時(shí)書寫idcanvas-id,而其他三個(gè)問題我的解決思路是:

能夠檢測(cè)小程序環(huán)境

與W3C標(biāo)準(zhǔn)一樣,實(shí)現(xiàn)Canvas & CanvasRenderingContext2D 兩個(gè)類:我們稱其為WxCanvas & WxCanvasRenderingContext2D

使用Object.defineProperty將調(diào)用轉(zhuǎn)換為賦值

當(dāng)調(diào)用有區(qū)別和沒有實(shí)現(xiàn)的函數(shù)時(shí),自動(dòng)處理。

檢測(cè)小程序環(huán)境
export function checkWX() {
    return __GLOBAL__DEBUG__WX__ === true || (typeof wx != "undefined" && typeof wx === "object");
}

忽略__GLOBAL__DEBUG__WX__,此變量用于調(diào)試。我們簡(jiǎn)單檢測(cè) wx 全局是否存在。代碼

實(shí)現(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;
        // 實(shí)例化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的實(shí)現(xiàn)還是比較簡(jiǎn)單的,主要注意的就是acquireContext方法,在這里我們判斷了小程序與非小程序環(huán)境,并獲得Canvas上下文。

下面我們看下WxCanvasRenderingContext2D的實(shí)現(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ù)省略,我們就拿前兩個(gè)舉例足以...
        
        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 兩個(gè)賦值屬性的兼容轉(zhuǎn)換??梢钥吹?,我們定義了通用方法_wxSetPropertyCallable,在小程序環(huán)境下,它會(huì)調(diào)用set[wxSetName](也就是setFillStyle和setStrokeStyle),而在普通環(huán)境下會(huì)正常的屬性賦值。

再來看我們的draw方法,同樣只在微信小程序中有效,但是遺憾的是每次繪制以后還是要調(diào)用draw方法一次,這是唯一的與W3C方法有區(qū)別的事情。不過,這主要是為了性能考慮,因?yàn)镴SBridge每一次的調(diào)用都是有成本的,如果每次設(shè)置我們都自動(dòng)的調(diào)用draw會(huì)大幅增加這個(gè)成本。

以上完成以后,我們就可以做到類似于W3C標(biāo)準(zhǔn),在小程序中繪圖了:

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();
一些遇到的問題

在實(shí)現(xiàn)兼容庫(kù)時(shí),有幾個(gè)比較棘手的問題。
首先是小程序并沒有實(shí)現(xiàn) measureText 方法,因此我們需要模擬一個(gè):

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 {//...}
    }

我們對(duì)漢字和非漢字進(jìn)行了處理。不過,需要承認(rèn)的是,即便是這樣,也與實(shí)際有一些不準(zhǔn)。

另外,textBaseline,textAlign 也需要特殊處理(當(dāng)前1.1版本已經(jīng)支持)

結(jié)束

由于時(shí)間和和篇幅有限,第一段文章就先寫到這里。有錯(cuò)誤或者不完善的情況請(qǐng)多多諒解和支持。也換用大家使用wx-chart 圖形庫(kù)并提出意見和建議。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/88916.html

相關(guān)文章

  • 2017-10-10 前端日?qǐng)?bào)

    摘要:前端日?qǐng)?bào)精選第期寫給前端應(yīng)屆生的職業(yè)規(guī)劃建議應(yīng)用編譯優(yōu)化之路進(jìn)階篇命名空間模式解析源碼解析之任務(wù)管理入門教程快速上手聊聊改變歷史中文正式發(fā)布,帶來種新的圖表類型關(guān)系圖解好好寫代碼吧使用手冊(cè)掘金發(fā)布在即將全面支持掘金仿懂球帝 2017-10-10 前端日?qǐng)?bào) 精選 【第1074期】寫給前端應(yīng)屆生的職業(yè)規(guī)劃建議webpack 應(yīng)用編譯優(yōu)化之路JS進(jìn)階篇--命名空間模式解析gulp源碼解析之任...

    myshell 評(píng)論0 收藏0
  • 在微信小程序中繪制圖表part1

    摘要:微信小程序中提供了的支持,利用自行繪制圖表。關(guān)注我的項(xiàng)目查看完整代碼。 微信小程序中圖表現(xiàn)狀 由于微信小程序本身框架的限制,很難集成目前已有的圖表工具,顯示圖表目前有兩種方案:1、服務(wù)器端渲染圖表,輸出圖片,微信小程序中直接顯示渲染好的圖片,比如highcharts提供了服務(wù)端渲染的能力hightcharts server render,這種方式需要后臺(tái)有一套渲染服務(wù),并且有一定的網(wǎng)絡(luò)...

    chaosx110 評(píng)論0 收藏0
  • 在微信小程序中繪制圖表(part2)

    摘要:本期大綱確定縱坐標(biāo)的范圍并繪制根據(jù)真實(shí)數(shù)據(jù)繪制折線相關(guān)閱讀在微信小程序中繪制圖表在微信小程序中繪制圖表關(guān)注我的項(xiàng)目查看完整代碼。相關(guān)閱讀在微信小程序中繪制圖表在微信小程序中繪制圖表 本期大綱 1、確定縱坐標(biāo)的范圍并繪制 2、根據(jù)真實(shí)數(shù)據(jù)繪制折線 相關(guān)閱讀:在微信小程序中繪制圖表(part1)在微信小程序中繪制圖表(part3) 關(guān)注我的 github 項(xiàng)目 查看完整代碼。 確定縱坐...

    canopus4u 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<