覆盖上下文。属性和功能
Override context. properties and functions
在创建我的 canvas 并获得 var context = can.getContext('2d')
之后,我的所有 canvas 工作都是通过设置属性或调用 context.
上的方法来完成的
我创建了一个多显示器屏幕截图工具,在单显示器上运行良好。但是现在,我正在处理多个显示器,因此屏幕 top/left 和比例不同。有时(比如在具有自定义 DPI 级别的 Windows 平台上)我必须缩放点。因此,我想通过覆盖函数传递所有属性和方法设置,以便在调用真正的 context.BLAH
之前,它会首先将坐标转换为按当前屏幕坐标缩放和偏移。
我可以缩放上下文,但这确实会导致抗锯齿的视觉问题。
这可能吗?
我尝试覆盖 context.lineWidth 和 context.fillRect 但我遇到了本机访问错误。
我想避免用:
包裹它
function lineWidth(a) {
doConvesionOnA(a)
ctx.lineWidth = a;
}
然后每次通过函数wrap调用。但如果这是唯一的方法,我会把它包起来。在为每个 属性 和方法创建包装之前,我只想先问一下,然后用我的包装替换我所有的 context.
调用。
方法可以,但属性不行,因为这些要经过验证,如果不是有效类型,值将被简单地拒绝,即不能设置函数来替换它(它也不能更新内部设置 if 它可以)。
我建议改用包装器对象。您可以通过绑定方法和包装属性使其与普通上下文兼容:
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;
...
同意@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 块,并通过使用 .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()
所有 属性 get/set 和所有方法调用都通过 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)]);
}
}
在创建我的 canvas 并获得 var context = can.getContext('2d')
之后,我的所有 canvas 工作都是通过设置属性或调用 context.
我创建了一个多显示器屏幕截图工具,在单显示器上运行良好。但是现在,我正在处理多个显示器,因此屏幕 top/left 和比例不同。有时(比如在具有自定义 DPI 级别的 Windows 平台上)我必须缩放点。因此,我想通过覆盖函数传递所有属性和方法设置,以便在调用真正的 context.BLAH
之前,它会首先将坐标转换为按当前屏幕坐标缩放和偏移。
我可以缩放上下文,但这确实会导致抗锯齿的视觉问题。
这可能吗?
我尝试覆盖 context.lineWidth 和 context.fillRect 但我遇到了本机访问错误。
我想避免用:
包裹它function lineWidth(a) {
doConvesionOnA(a)
ctx.lineWidth = a;
}
然后每次通过函数wrap调用。但如果这是唯一的方法,我会把它包起来。在为每个 属性 和方法创建包装之前,我只想先问一下,然后用我的包装替换我所有的 context.
调用。
方法可以,但属性不行,因为这些要经过验证,如果不是有效类型,值将被简单地拒绝,即不能设置函数来替换它(它也不能更新内部设置 if 它可以)。
我建议改用包装器对象。您可以通过绑定方法和包装属性使其与普通上下文兼容:
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;
...
同意@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 块,并通过使用 .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()
所有 属性 get/set 和所有方法调用都通过 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)]);
}
}