覆盖上下文。属性和功能

时间:2016-04-24 01:10:30

标签: javascript html5 canvas

创建我的画布并获得var context = can.getContext('2d')后,我的所有画布工作都是通过设置属性或在context.

上调用方法来完成的

我创建了一个多监视器截图工具,在单个监视器上运行良好。但是现在,我正在处理多个显示器,因此不同的屏幕顶部/左侧和刻度。有时候(比如在具有自定义DPI级别的Windows平台上)我必须扩展积分。因此,我希望通过覆盖函数传递属性和方法的所有设置,以便在调用真实context.BLAH之前,它将首先将坐标转换为缩放并偏移当前屏幕坐标。

我可以缩放上下文,但这确实会导致抗锯齿的视觉问题。

这可能吗?

我尝试覆盖context.lineWidth和context.fillRect,但我遇到了原生访问错误。

我想避免用:

包装它
function lineWidth(a) {
doConvesionOnA(a)
ctx.lineWidth = a;
}

然后每次通过函数换行调用。但如果这是唯一的方法,我会把它包裹起来。在我为每个属性和方法创建一个包装然后用我的包裹替换所有context.调用之前,我只想先问一下。

2 个答案:

答案 0 :(得分:2)

有可能使用方法,但不能使用属性,因为这些通过验证,如果不是有效类型,则简单地拒绝该值,即无法将函数设置为替换它(并且它也不能更新内部设置如果可以)。

我建议改为使用包装器对象。您可以通过绑定方法和包装属性使其与普通上下文兼容:

function MyContext(ctx) {

    // Methods

    this.moveTo = ctx.moveTo.bind(ctx);
    this.lineTo = ctx.lineTo.bind(ctx);
    // etc.

    // Properties

    Object.defineProperty(this, "lineWidth", {
        get: function() {return ctx.lineWidth},
        set: function(value) {
            // do something magic with value
            ctx.lineWidth = value
        }
    });

    // etc.
}

如果您想更改方法的值:

this.moveTo = function(x, y) {
    // alter here
    ctx.moveTo(x, y);
};

您也可以使用apply(),它比传递实际参数更灵活但更慢:

this.arc = function() {
    ctx.arc.apply(ctx, arguments)
};

这可能有点乏味,但让你完全控制传递到真实环境的内容。然后简单地创建一个对象的实例,并将其用作2D上下文:

var myCtx = new MyContext(ctx);
myCtx.lineTo(100, 100);
myCtx.lineWidth = 20;
...

答案 1 :(得分:1)

同意@ K3N的建议以包装上下文。

这是我从CanvasRendingContext2D记录器中获取的一些代码,显示了您开始包装CanvasRendingContext2D的速度:

function LoggedContext(canvas) {
    var self = this;
    this.canvas=canvas;
    this.context=canvas.getContext('2d');
    this.imageURLs=[];
    this.fillStyles=[];
    this.logs=[];
    this.commands=[];
    this.funcs={};
    this.init(self);
}

LoggedContext.prototype.init方法中,为每个属性创建get / set块,并通过将所有收到的参数传递给" real"来镜像每个上下文方法。上下文使用.apply

LoggedContext.prototype.init=function(self){

    // create get/sets for properties
    var properties=['strokeStyle','lineWidth','font','globalAlpha',
        'globalCompositeOperation','shadowColor','shadowBlur',
        'shadowOffsetX','shadowOffsetY','lineCap','lineJoin',
        'miterLimit','textAlign','textBaseline'];

    for(var i=0;i<properties.length;i++){
        (function(i) {
            Object.defineProperty(self, i, {
                get: function () {
                    return this.context[i];
                },
                set: function (val) {
                    this.log(i,val,true);
                    this.context[i]=val;
                }
            })
        })(properties[i]);
    }

    // create mirror methods that pipe arguments to the real context
    var methods = ['arc','beginPath','bezierCurveTo','clearRect','clip',
      'closePath','fill','fillRect','fillText','lineTo','moveTo',
      'quadraticCurveTo','rect','restore','rotate','save','scale','setTransform',
      'stroke','strokeRect','strokeText','transform','translate','putImageData'];

    for (var i=0;i<methods.length;i++){   
        var m = methods[i];
        this[m] = (function(m){
            return function () {
                this.context[m].apply(this.context, arguments);
                this.log(m,arguments);
                return(this);
        };}(m));
    }

    // mirror methods that have return values
    var returnMethods = ['measureText','getImageData','toDataURL',
      'isPointInPath','isPointInStroke','createImageData'];

    for (var i=0;i<returnMethods.length;i++){   
        var m = returnMethods[i];
        this[m] = (function(m){
            return function () {
                return(this.context[m].apply(this.context, arguments));
        };}(m));
    }

    // In this example code ...
    // These Properties & Methods requiring special handling have 
    // been removed for brevity & clarity
    //
    //  .fillStyle
    //  .strokeStyle
    //  .drawImage
    //  .createLinearGradient
    //  .createRadialGradient
    //  .createPattern


} // end init()

所有属性获取/设置,所有方法调用都通过LoggedContext.prototype.log方法引导。

出于您的目的,您可以在get / set块中进行调整,也可以在.log方法中方便地进行调整,因为所有内容都是通过.log方法传输的。

LoggedContext.prototype.log=function(command,Args,isProperty){
    var commandIndex=this.commands.indexOf(command);
    if(commandIndex<0){
        this.commands.push(command);
        commandIndex=this.commands.length-1
    }
    if(isProperty){
        this.logs.push([commandIndex,Args]);
    }else{
        this.logs.push([commandIndex,Array.prototype.slice.call(Args)]);
    }
}