简体   繁体   English

在HTML5画布上下文上将钩子更改为lineWidth

[英]Hook changes to lineWidth on HTML5 canvas context

I want to enforce a miterLimit in actual pixels rather than as a ratio of the lineWidth. 我想在实际像素中而不是作为lineWidth的比率来实施miterLimit。 To do this, I'd like to hook any changes to lineWidth, and set the miterLimit simultaneously and automatically. 为此,我想对lineWidth进行任何更改,并同时自动设置miterLimit。 I've used custom setters on objects before, but if I replace the lineWidth setter, I don't know of any way to actually pass the value to set on through to the actual canvas context. 我以前在对象上使用过自定义设置器,但是如果我替换了lineWidth设置器,我不知道有什么方法可以实际将设置值传递给实际的画布上下文。

Is there some way (compatible on IE9+) that I can listen to changes to a given key on an object without changing the behavior of setting that value? 是否有某种方式(与IE9 +兼容),我可以在不更改设置该值的行为的情况下侦听对象上给定键的更改?

Your getter/setter idea is a good one... 您的getter / setter想法是一个好主意...

How about just adding a property definition to your context object? 仅向您的上下文对象添加属性定义呢?

Add a myLineWidth property to your context object and then set the linewidth using context.myLineWidth instead of context.lineWidth . myLineWidth属性添加到上下文对象,然后使用context.myLineWidth而不是context.lineWidth设置线宽。

Some example code: 一些示例代码:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

Object.defineProperty(ctx, 'myLineWidth', {
    get: function() {
        return(this.lineWidth);
    },
    set: function(newWidth) {
        this.lineWidth=newWidth;
        console.log("Executed myLineWidth setter: ",this.lineWidth);        
    }
});

ctx.myLineWidth=5;
ctx.strokeRect(100,100,50,50);

Alternate Method using Encapsulation: 使用封装的替代方法:

JavaScript does have true inheritance so it's not possible to inherit & override lineWidth . JavaScript确实具有真正的继承,因此无法继承和覆盖lineWidth

The next best thing would be encapsulating the context object. 接下来最好的方法是封装上下文对象。 Then all coders can use the encapsulated version of the context using the standard property and method syntax (no need for myLineWidth). 然后,所有编码人员都可以使用标准属性和方法语法使用上下文的封装版本(不需要myLineWidth)。 If needed, here's a how-to: http://aboutcode.net/2011/10/04/efficient-encapsulation-of-javascript-objects.html . 如果需要,这里有一个方法: http : //aboutcode.net/2011/10/04/efficiency-encapsulation-of-javascript-objects.html

I did a similar encapsulation in order to log the context drawings. 为了记录上下文图纸,我做了类似的封装。 Below, I've tried to snip the encapsulation code from one of my projects. 下面,我尝试从我的一个项目中截取封装代码。 You can ignore my special handling of drawImage and gradients as I needed to grab values from these that you won't need to grab--just add those methods to the returnMethods[] array. 您可以忽略我对drawImage和渐变的特殊处理,因为我需要从不需要获取的值中获取值,只需将这些方法添加到returnMethods []数组中即可。

Some example code for you to start with: 您可以从以下示例代码开始:

// Log all context drawings
// Creates a proxy class wrapping canvas context
function LoggedContext(canvas,context) {
    var self = this;
    this.canvas=canvas;
    this.context=context;
    this.imageURLs=[];
    this.log=[];
    this.gradients=[];
    this.patterns=[];
    this.init(self);

}

// maintain urls of images used
LoggedContext.prototype.imageIndex=function(url){
    var i=a.indexOf(url);
    // found
    if(i>-1){ return(i); }
    // not found -- added
    a.push(url);
    return(a.length-1);
}
///////////////////////////////////////////
// These methods require special handling
// (drawImage:need image.src, gradients:need gradDefs & colors)
//
LoggedContext.prototype.drawImage=function(){
    this.context.drawImage.apply(this.context,arguments);
    var args = Array.prototype.slice.call(arguments);
    args[0]=arguments[0].src;
    args.unshift(2,"drawImage");
    var sArgs=JSON.stringify(args);
    this.log.push(sArgs);
    return(this);
}
//
LoggedContext.prototype.createLinearGradient =function(x1,y1,x2,y2){
    var gradient=this.context.createLinearGradient(x1,y1,x2,y2);
    gradient.context=this;
    gradient.gradientID=this.gradients.length;
    this.gradients.push({line:{x1:x1,y1:y1,x2:x2,y2:y2},stops:[]});
    gradient.baseAddColorStop=gradient.addColorStop;
    gradient.addColorStop=function(stop,color){
        this.context.gradients[this.gradientID].stops.push({stop:stop,color:color});
        this.baseAddColorStop(stop,color);
    }
    return(gradient);
}
//
LoggedContext.prototype.createPattern =function(i,r){
    var pattern=this.context.createPattern(i,r);
    pattern.patternID=this.patterns.length;
    this.patterns.push({src:i.src,repeat:r});
    return(pattern);
}
//
LoggedContext.prototype.createRadialGradient =function(sx,sy,sr,ex,ey,er){
    var gradient=this.context.createRadialGradient(sx,sy,sr,ex,ey,er);
    gradient.context=this;
    gradient.gradientID=this.gradients.length;
    this.gradients.push({circles:{sx:sx,sy:sy,sr:sr,ex:ex,ey:ey,er:er},stops:[]});
    gradient.baseAddColorStop=gradient.addColorStop;
    gradient.addColorStop=function(stop,color){
        this.context.gradients[this.gradientID].stops.push({stop:stop,color:color});
        this.baseAddColorStop(stop,color);
    }
    return(gradient);
}

// load the proxy object with all properties & methods of the context
LoggedContext.prototype.init=function(self){

    // define public context properties
    var properties={
        //
        fillStyle:"black",
        strokeStyle:"black",
        lineWidth:1,
        font:"10px sans-serif",
        //
        globalAlpha:1.00,
        globalCompositeOperation:"source-over",
        //
        shadowColor:"black",
        shadowBlur:0,
        shadowOffsetX:0,
        shadowOffsetY:0,
        //
        lineCap:"butt",   // butt,round,square
        lineJoin:"miter", // miter,round,miter
        miterLimit:10,
        //
        textAlign:"start",
        textBaseLine:"alphabetic",
    };

    // encapsulate public properties
    for (var i in properties) {
        (function(i) {
            if(!(i=="fillStyle")){
                Object.defineProperty(self, i, {
                    get: function () {
                        return properties[i];
                    },
                    set: function (val) {
                        this.log.push(JSON.stringify([1,i,val]));
                        properties[i] = val;
                        this.context[i]=val;
                    }
                })
            }else{
                Object.defineProperty(self, i, {
                    get: function () {
                        return properties[i];
                    },
                    set: function (val) {
                        if(typeof val ==="object"){
                            if(val.gradientID>=0){
                                this.log.push(JSON.stringify([1,i,"gradient",val.gradientID]));
                            }else if(val.patternID>=0){
                                this.log.push(JSON.stringify([1,i,"pattern",val.patternID]));
                            }
                        }else{
                            this.log.push(JSON.stringify([1,i,val]));
                        }
                        properties[i] = val;
                        this.context[i]=val;
                    }
                })
            }
        })(i);
    }

    // define public context methods
    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'];

    // encapsulate public methods
    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);
                // "arguments" is not a real array--so convert it
                var args = Array.prototype.slice.call(arguments);
                args.unshift(2,m);
                var sArgs=JSON.stringify(args);
                this.log.push(sArgs);
                return(this);
        };}(m));
    }

    // define context methods that return values
    var returnMethods = ['measureText','getImageData','toDataURL',
      'isPointInPath','isPointInStroke'];

    // encapsulate return methods
    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));
    }

}  // end init()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM