構建Canvas矢量圖形渲染器(五) —— 添加圖片,幾何元素的外接矩形,初步性能優化,性能測試。


    本系列目錄大家有需要的就看看哈。

    本節主要解決的有三個問題,求任意幾何圖形的外接矩形。這個外接矩形會在性能優化時候用到,當然不僅僅只在這一方面使用。最后對初步優化過的渲染器進行壓力測試

    還是先上demo,之前的demo在firefox下滾動縮放漏做了。現在更新的可以再ie9,chrome,firefox中使用(當然需要支持canvas)。

    1.第一個demo表示了幾何圖形的外接矩形。大家發現點和圖片沒有外接矩形,因為他們的范圍表示成一個點,均為中心點。

    為什么圖片也沒有范圍呢?因為異步的圖片加載使我們無法在一開始就得知圖片的寬高。

 

 

    2.第二個demo 為性能測試demo,點擊測試按鈕會隨機添加500個點和500個五角星,多次點擊多次添加(大家悠着些啊,別把瀏覽器點崩了。。)。

    可以發現在我們放大2倍左右后,性能還是比較不錯的,因為這里做了個小優化,通過上面我們取得的外接矩形和當前視圖的范圍做相交比較,只有和當前視圖范圍相交的幾何圖形才會被繪制。

 

     
    恩恩,大家測試完后對canvas的矢量繪圖是更有信心了?還是覺得用canvas做矢量圖形渲染本身就是一BUG?希望大家寫寫自己的看法,當然這個只是初步的性能優化有待提高的地方還是很多的。

1.下面就開始對我們上面提到的外接矩形做一個詳細的描述

     在渲染器中規定所有繼承自geometry基類的幾何類型必須要有一個計算其范圍的方法(getBounds),當然我們的幾何圖形不同,計算范圍的方法也就不同。

 1.下面我們先看最簡單的圓的范圍計算方法。

Circle.prototype.getBounds = function () {
    if(!this.bounds) {
        this.bounds = new CanvasSketch.Bounds(this.x - this.radius, this.y - this.radius, this.x + this.radius, this.y + this.radius);
        return this.bounds;
    } else {
        return this.bounds;
    }
}

   大家一看代碼就明白了,這不是初中、小學數學么。。

2.Path計算范圍的方法。

    在現在所有的幾何圖形,除了point(點)、circle(圓)、img(圖像)是直接繼承自geometry基類的其余的均繼承自line。這樣只要我們得到line計算范圍的方法就可以解決所有的問題了。

Line.prototype.getBounds = function () {
    if(!this.bounds) {
        var p0 = this.points[0];
        this.bounds = new CanvasSketch.Bounds(p0.x, p0.y, p0.x, p0.y);
        for(var i = 1, len = this.points.length; i< len; i++) {
            var point = this.points[i];
            var bounds = new CanvasSketch.Bounds(point.x, point.y, point.x, point.y);
            this.bounds.extend(bounds);
        }
    }
    return this.bounds;
}

    line計算范圍的方法其實也不難,我們遍歷所有的點,把每個點都當做成一個范圍。通過bounds.extend方法將所有的范圍組合到一起便是我們需要的外接矩形。

    extend方法如下:

CanvasSketch.Bounds.prototype.extend = function (bounds) {
    if(this.left > bounds.left) {
        this.left = bounds.left;
    }
    if(this.bottom > bounds.bottom) {
        this.bottom = bounds.bottom;
    }
    if(this.right < bounds.right) {
        this.right = bounds.right;
    }
    if(this.top < bounds.top) {
        this.top = bounds.top;
    }
}

    這個方法中我們就是比較大小,把適合的位置信息賦值給我們要擴展范圍的bounds。

    通過上面的兩個方法就讓我們獲得了所有幾何圖形的外接矩形,下面我們討論如何用外接矩形優化性能。

2.渲染器初步優化。

    本次的優化其實非常簡單,也是所有圖形庫必須的優化:繪制能看見的圖形,其余的不進行考慮。在圖形學中稱這個過程為“二維裁剪”,有興趣的同學可以baidu一下。

    有了范圍后,我們需要一個判斷兩個范圍相交的函數:

CanvasSketch.Bounds.prototype.intersect = function (bounds) {
    var inBottom = (
        ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) ||
        ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top))
    );
    var inTop = (
        ((bounds.top >= this.bottom) && (bounds.top <= this.top)) ||
        ((this.top > bounds.bottom) && (this.top < bounds.top))
    );
    var inLeft = (
        ((bounds.left >= this.left) && (bounds.left <= this.right)) ||
        ((this.left >= bounds.left) && (this.left <= bounds.right))
    );
    var inRight = (
        ((bounds.right >= this.left) && (bounds.right <= this.right)) ||
        ((this.right >= bounds.left) && (this.right <= bounds.right))
    );
    intersects = ((inBottom || inTop) && (inLeft || inRight));
    return intersects;
}

    通過這個函數我們判斷出兩個范圍是否相交,下面正式的優化開始:

Canvas.prototype.redraw = function(){
    this.context.clearRect(0, 0, this.layer.size.w, this.layer.size.h);
    var geometry;
    if(!this.lock){
        for(var id in this.geometrys){
            geometry = this.geometrys[id][0];
            var bounds = geometry.getBounds();
            if(this.layer.bounds.intersect(bounds)) {            
                style = this.geometrys[id][1];
                this.draw(geometry, style, geometry.id); 
            }
        }
    }    
}

    我們在繪制每一個圖形之前,判斷其范圍是否和當前視圖范圍有相交,如果相交才繪制,不相交什么也不做了。

    這樣我們基本的優化就做完了,計划以后有更高級的優化,現在還在斟酌方案。

3.支持圖片添加

1.有了這么一個框架我們添加新的圖形元素變的非常簡單,圖像類也是如此。

//CLASS: 顯示圖像類。
function Img(point, image) {
    Geometry.apply(this, arguments);
    this.point = point;
    if(typeof image == Image) {
        this.useUrl = false;
        this.image = image;
    }else {
        this.useUrl = true;
        this.image = image;
    }
}

Img.prototype = new Geometry();

Img.prototype.geoType = "Img";

Img.prototype.getBounds = function () {
    return new CanvasSketch.Bounds(this.point.x, this.point.y, this.point.x, this.point.y);
}

    我們這里支持直接傳入image對象前提是你必須保證他已經請求完成)。我們還可以傳入圖像的url,程序內部幫大家請求圖片;在請求完成后圖片將被設置為Img對象的一個資源,以后再使用的時候就不用再次請求了。

2.渲染添加對應的解析圖像的方法。

//針對圖片的繪制方法。
Canvas.prototype.drawImage = function(geometry, style, id){
    var canvas = this;
    if(!geometry.useUrl) {
        var img = geometry.image;
        imageLoad();
    }else {
        var img = new Image();
        img.onload = imageLoad;
        img.loadErro = imageErro;
        img.src = geometry.image;    
    }
    
    function imageLoad() {
        canvas.setCanvasStyle("fill", style);
        var fixedSize = style.fixedSize;
        var pt = canvas.getLocalXY(geometry.point);
        var width = style.width || img.width;
        var height = style.width || img.height;
        if(fixedSize) {
            var offsetX = width / 2;
            var offsetY = height / 2;
            canvas.context.drawImage(img, pt.x - offsetX, pt.y - offsetY, width, height);
        }else {
            var res = canvas.layer.getRes();
            var offsetX = width / 2 / res;
            var offsetY = height / 2 / res;
            canvas.context.drawImage(img, pt.x - offsetX, pt.y - offsetY, width / res, height / res);
        }
        if(geometry.useUrl) {
            geometry.useUrl = false;
            geometry.image = img;
        }
        canvas.setCanvasStyle("reset");
    }
    
    function imageErro() {
    
    }
}

    這里我們使用了一個閉包的小技巧,我們用局部變量canvas保存了對this的引用(在圖片加載完成后this已經指向圖像本身了)。

    添加了圖像加載完成事件,其中我們首先將圖像繪制到canvas畫布上(這里我們添加了一個style屬性:fixedSize;如果fixedSize設置為true,我們就不對圖像進行縮放了)。

    之后將圖像作為一個資源保存起來。

    這樣我們的圖像類也就制作完成了。

    呼呼,一天寫了兩篇隨筆。下次的隨筆給大家帶來些高級的應用二三維一體化——通過我們設計好的渲染器實現幾何圖形的二維三維的互換(PS.由於還沒有怎么實現、可能編碼周期有點長大、敬請關注~)

    嘗試一下:下載這次的源碼,修改里面的demo開開能否有新的發現(bug?呵呵~)

    下隨筆次預告:1. 矢量圖形二三維一體化。

   本次隨筆的所有源碼+demo請點擊下載



    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM