我想在实际像素中强制执行miterLimit,而不是lineWidth的比率。为此,我想将任何更改挂钩到lineWidth,并同时自动设置miterLimit。我以前在对象上使用过自定义setter,但是如果我替换lineWidth setter,我就不知道如何实际传递值来设置实际的画布上下文。
是否有某种方式(在IE9 +上兼容)我可以监听更改对象上的给定键而不更改设置该值的行为?
答案 0 :(得分:0)
你的getter / setter想法很好......
如何只将属性定义添加到上下文对象中?
向您的上下文对象添加myLineWidth
属性,然后使用context.myLineWidth
而不是context.lineWidth
设置线宽。
一些示例代码:
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);
使用封装的替代方法:
JavaScript确实具有真正的继承性,因此无法继承&覆盖lineWidth
。
下一个最好的事情是封装上下文对象。然后,所有编码器都可以使用标准属性和方法语法(不需要myLineWidth)来使用上下文的封装版本。如果需要,这里有一个操作方法:http://aboutcode.net/2011/10/04/efficient-encapsulation-of-javascript-objects.html。
我做了类似的封装以记录上下文绘图。下面,我试图从我的一个项目中截取封装代码。您可以忽略我对drawImage和渐变的特殊处理,因为我需要从这些中获取您不需要抓取的值 - 只需将这些方法添加到returnMethods []数组即可。
您可以从以下开始的一些示例代码:
// 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()