Let’s first look at a normal canvas drawing grammar:
ctx.arc(centerX, centerY,radius,0,PI*2,true);
ctx.shadowColor = 'rgba(0,0,0,0.5)';
ctx.shadowBlur = "10";
ctx.fill();
ctx.beginPath();
ctx.shadowColor = 'rgba(0,0,0,0)';
ctx.moveTo(centerX-radius,centerY);
ctx.lineTo(centerX-radius,centerY - 50);
ctx.lineTo(centerX+radius,centerY - 50);
ctx.lineTo(centerX+radius,centerY);
// ctx.lineTo(centerX-radius,centerY);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = 'rgba(255,0,0,1)';
ctx.arc(centerX, centerY-50,radius,0,PI*2,true);
ctx.fill();
There are two things that I am not happy with the native syntax of canvas: 1. Each sentence has written ctx (i.e., the context2d object of canvas), and 2. Each function or property has to occupy one line, wasting space.
I have a lot of appreciation for jQuery's chain syntax, such as:
$('#div1').show(300).html(p).delay(3000).slideUp(300).remove();
So, I also want to use this syntax to do canvas drawing:
ctx.moveTo(500,0).lineTo(500,500).strokeStyle('#f00').stroke();
There is a way to simulate a context2d object. This object supports all native context2d methods, but also supports chained methods.
However, there can't be too much code, as there will be more no one likes to use it.
The following is the complete code segment. I named this class XtendCanvas (it starts with X again):
// Let canvas support chain syntax, from ten years of light
~function () {var pro = ['save','restore', 'scale', 'rotate', 'translate', 'transform', 'createLinearGradient', 'createRadialGradient', 'getLineDash', 'clearRect', 'fillRect', 'beginPath', 'closePath', 'moveTo', 'lineTo', 'lineTo', 'quadraticCurveTo', 'bezierCurveTo', 'arcTo', 'rect', 'arc', 'fill', 'stroke', 'clip', 'isPointInPath', 'measureText', 'clearShadow', 'fillText', 'strokeText', 'strokeRect', 'drawImage', 'drawImageFromRect', 'putImageData', 'createPattern', 'createImageData', 'getImageData', 'lineWidth', 'strokeStyle', 'globalAlpha','fillStyle','font','shadowOffsetX','shadowOffsetY','shadowBlur','shadowColor','lineCap','lineJoin','miterLimit'];
function XtendCanvas (canvas) {
var ctx = canvas.getContext('2d'),
fn = function(){},
fnP = fn.prototype;
for(var j = 0,p=pro[0];p;p=pro[j++]) {
fn.prototype[p] = function (p) {
return function () {
var args = Array.prototype.slice.call(arguments);
// console.log(args);
if(typeof ctx[p] == 'function') {
ctx[p].apply(ctx,args);
} else {
ctx[p] = args+'';
}
return fnP;
};
}(p);
}
return new fn;
};
window.XtendCanvas = XtendCanvas;
}();
The usage method is very simple. Pass it a canvas object and it will return an object similar to context2d. You can use it like ordinary context2d, but the difference is that it supports chain syntax:
var cvs = document.getElementById('cvs');
var ctx = XtendCanvas(cvs);
ctx.moveTo(500,0).lineTo(500,500).strokeStyle('#f00').stroke();
This way you can put all operations in one sentence, and you can interrupt at any time, do other things, and then continue.
This code is not an enhancement to canvas, it simply supports chain syntax. But the advantage is that there is little code and it can be embedded in any JS library. I hope to get a recommendation from you here
There is definitely something worth improving in the code, and everyone can improve it on their own. But - I don't forget to dig wells when I draw water. I hope everyone remembers me. The most important thing is the idea, right? Here is the idea:
As you can see, the longest part of the code is the array pro that saves the method name, and the core code is actually very short. Why do I want to build such an array?
Originally, I also wanted to inherit all native methods directly from CanvasRenderingContext2D, but each browser traversed the CanvasRenderingContext2D, and the results were inconsistent. If I inherit them directly, then when you want to use the method in Chrome to execute it, an error will be reported.
So I just extracted the common, unobjective methods and attribute names in CanvasRenderingContext2D. There is no way, I can only create a fixed array - everyone can decide to add your method to it.
The methods and attributes are extracted, and then the native methods are added to my new object. I built an empty function called fn and placed my method.
Since these elements in the array have both functions and attributes, I judged in the loop whether it is a function. If it is a function, it will be executed with parameters; if it is not a function - then it must be a property, so I assign the parameters to this property.
In this way, when you encounter setting the canvas attribute, you don’t need to interrupt the link, just pass the attribute value as a parameter, for example:
ctx.strokeStyle('#f00')
Finally, the key is to return fn. This trick was learned from jQuery and is the key to supporting chain syntax.
This paragraph uses anonymous functions, closures, prototypes, and the strange for loops I have talked about in previous articles.
It seems quite simple to say, but I have thought about it for a long time, hoping it will be useful to everyone.
During the process of writing code, I found that Chrome is doing very well. It has a string of functions starting with set, such as setStrokeColor, setLineCap and other functions. By passing parameters to them, it can replace the corresponding attributes such as strokeStyle and lineCap. In other words, its canvas is full of functions and no attributes, so I don't have to judge whether it is a function or a attribute. But there is no such thing in firefox, so I can only use the previous ideas.
I'll also put out that string of set functions :var bak = ['setTransform','setAlpha', 'setCompositeOperation', 'setLineWidth', 'setLineCap', 'setLineJoin', 'setMiterLimit', 'setLineDash','setShadow','setStrokeColor','setFillColor'];
They can understand their uses at a glance. You can also select some pro arrays added to the previous code.
Finally, I'm wondering why my code isn't highlighted. . . If you see the end, then give me a recommendation and let me be vain.