Javascript 如何简化此代码?
我刚开始学习canvas,到目前为止已经尝试了几个练习,但我的代码总是太长,而且可能不必要地复杂。我有一个四叶三叶草画下面的代码,想知道如何简化它。有什么建议吗 提前谢谢你Javascript 如何简化此代码?,javascript,html,canvas,html5-canvas,Javascript,Html,Canvas,Html5 Canvas,我刚开始学习canvas,到目前为止已经尝试了几个练习,但我的代码总是太长,而且可能不必要地复杂。我有一个四叶三叶草画下面的代码,想知道如何简化它。有什么建议吗 提前谢谢你 var clover = document.getElementById("clover"); var ctx = clover.getContext("2d"); //style: ctx.strokeStyle = "#006600"; ctx.lineWidth = 0.3; ctx.beginPath(); c
var clover = document.getElementById("clover");
var ctx = clover.getContext("2d");
//style:
ctx.strokeStyle = "#006600";
ctx.lineWidth = 0.3;
ctx.beginPath();
ctx.moveTo(115,80);
ctx.bezierCurveTo(20,100,200,100,235,135);
ctx.stroke();
//First leaf:
ctx.strokeStyle = "black";
ctx.lineWidth = 0.8;
ctx.fillStyle = "#7BA32D";
ctx.beginPath();
ctx.moveTo(55,70);
ctx.quadraticCurveTo(20,100,115,80);
ctx.stroke();
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#7BA32D";
ctx.beginPath();
ctx.moveTo(55,70);
ctx.quadraticCurveTo(40,30,115,80);
ctx.stroke();
ctx.closePath();
ctx.fill();
// Second leaf:
ctx.fillStyle = "#7BA32D";
ctx.beginPath();
ctx.moveTo(115,80);
ctx.quadraticCurveTo(80,20,130,50);
ctx.stroke();
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#7BA32D";
ctx.beginPath();
ctx.moveTo(115,80);
ctx.quadraticCurveTo(200,40,130,50);
ctx.stroke();
ctx.closePath();
ctx.fill();
// Third leaf:
ctx.fillStyle = "#7BA32D";
ctx.beginPath();
ctx.moveTo(115,80);
ctx.quadraticCurveTo(235,60,185,85);
ctx.stroke();
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#7BA32D";
ctx.beginPath();
ctx.moveTo(115,80);
ctx.quadraticCurveTo(190,115,185,85);
ctx.stroke();
ctx.closePath();
ctx.fill();
// Fourth leaf:
ctx.fillStyle = "#7BA32D";
ctx.beginPath();
ctx.moveTo(115,80);
ctx.quadraticCurveTo(180,135,110,115);
ctx.stroke();
ctx.closePath();
ctx.fill();
ctx.fillStyle = "#7BA32D";
ctx.beginPath();
ctx.moveTo(115,80);
ctx.quadraticCurveTo(60,130,110,115);
ctx.stroke();
ctx.closePath();
ctx.fill();
// lines on the leaves:
ctx.strokeStyle = "#006600";
ctx.lineWidth = 0.3;
ctx.beginPath();
ctx.moveTo(115, 80);
ctx.lineTo(65, 71);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(115, 80);
ctx.lineTo(127, 55);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(115, 80);
ctx.lineTo(175, 85);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(115, 80);
ctx.lineTo(110, 110);
ctx.stroke();
ctx.closePath();
编写一个或多个函数来执行重复的操作。找出他们需要采取哪些参数才能处理略有不同的情况。然后使用正确的参数调用函数。例如,表单的代码
ctx.beginPath();
ctx.moveTo(115, 80);
ctx.lineTo(110, 110);
ctx.stroke();
ctx.closePath();
将被写为函数
function line(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.closePath();
}
并称为
line(115, 80, 110, 110);
编写一个或多个函数来执行重复的操作。找出他们需要采取哪些参数才能处理略有不同的情况。然后使用正确的参数调用函数。例如,表单的代码
ctx.beginPath();
ctx.moveTo(115, 80);
ctx.lineTo(110, 110);
ctx.stroke();
ctx.closePath();
将被写为函数
function line(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.closePath();
}
并称为
line(115, 80, 110, 110);
表达性语言
Javascript以其表达的灵活性而闻名,并且可以允许大多数其他语言中找不到的编码样式
使用Path2D
代码的第一个想法是使用Path2D对象定义绘图命令。它使用与SVG path命令类似的语法
但是为什么不创建自己的命令列表呢
命名样式
首先,让我们找到所有样式并命名它们
var styles = {
dGreen : {
strokeStyle : "#006600",
lineWidth : 0.3,
},
black : {
strokeStyle : "black",
fillStyle : "#7BA32D",
lineWidth : 0.8,
}
}
现在,您可以使用命名样式,如果您将属性命名为与2D API相同的名称,则设置样式非常容易
function setStyle(style){
Object.keys(style).forEach(prop => ctx[prop] = style[prop]);
},
setStyle(styles.black); // sets the style black
如果你想使用你当时没有想到的属性,你不必编写代码,只需设置属性就可以了
styles.black.lineJoin = "round";
setStyle(styles.black); // sets the style black
自定义命令列表。
对于图形命令,您正在多次执行同一组操作。在SVG中,命令是moveto的单个字符M,后跟x,y坐标
我们也可以这样做。命令将是一个字符串,由分隔,然后拆分为一个数组。您可以根据需要从阵列中切换每个命令
首先是对每个命令都有函数的命令对象。在这种情况下,M代表moveTo,L代表lineTo。它获取用于从中获取坐标的数组
var commands = {
M(array){
ctx.moveTo(array.shift(),array.shift());
},
L(array){
ctx.lineTo(array.shift(),array.shift());
}
}
然后用我们的新命令定义一条路径,移动到10,10,然后移动到100100
var path = "M,10,10,L,100,100";
现在我们只需要解析和解释路径
function drawPath(path){
// split the command string into parts
var commandList = path.split(",");
// while there are commands
while(commandList.length > 0){
// use the next command to index the command
// and call the function it names passing the command list so
// it can get the data it needs
commands[commandList.shift()](commandList);
} // do that until there is nothing on the command list
}
现在,您只需提供命令字符串即可绘制所需内容。因为您可以定义命令,所以可以根据需要创建复杂或简单的命令
稍微复杂一点
下面是我创建的用于绘制图像的命令函数和绘图函数
// define draw commands
var drawFuncs = {
getN(a,count){ return a.splice(0,count); }, // gets values from array
M(a){ ctx.moveTo(...this.getN(a,2)); }, // move to
C(a){ ctx.bezierCurveTo(...this.getN(a,6)); }, // bezier curve
Q(a){ ctx.quadraticCurveTo(...this.getN(a,4)); },// quad curve
S(){ ctx.stroke(); }, // stroke
P(){ ctx.closePath(); }, // close path
F(){ ctx.fill(); }, // fill
B(){ ctx.beginPath(); }, // begin path
l(a) { // line segment
ctx.beginPath();
ctx.moveTo(...this.getN(a,2));
ctx.lineTo(...this.getN(a,2));
ctx.stroke();
},
St(a){ // set style
var style = styles[a.shift()];
Object.keys(style).forEach(prop=>ctx[prop] = style[prop]);
},
}
// Takes command string and draws what is in it
function draw(shape){
var a = shape.split(",");
while(a.length > 0){
drawFuncs[a.shift()](a);
}
}
您可以将该代码放入一个单独的库中,然后在集中精力渲染时将其忘掉
现在,您可以通过自己定制的声明性语言进行渲染
定义样式
// define named styles
var styles = {
dGreen : {
strokeStyle : "#006600",
lineWidth : 0.3,
},
black : {
strokeStyle : "black",
fillStyle : "#7BA32D",
lineWidth : 0.8,
}
}
创建命令列表并绘制
draw([
"St,dGreen,B,M,115,80,C,20,100,200,100,235,135,S",
"St,black,B,M,55,70,Q,20,100,115,80",
"M,55,70,Q,40,30,115,80",
"M,115,80,Q,80,20,130,50",
"M,115,80,Q,200,40,130,50",
"M,115,80,Q,235,60,185,85",
"M,115,80,Q,190,115,185,85",
"M,115,80,Q,180,135,110,115",
"M,115,80,Q,60,130,110,115,S,P,F",
"St,dGreen",
"l,115,80,65,71",
"l,115,80,127,55",
"l,115,80,175,85",
"l,115,80,110,110",
].join(","));
演示
注意:所有代码都是用ES6编写的,需要Babel或类似的工具才能在传统浏览器上工作
//定义绘图命令
var drawFuncs={
getNa,count{return a.0,count;},//从数组中获取值
Ma{ctx.moveTo…this.getNa,2;},//移动到
Ca{ctx.bezier Curveto…this.getNa,6;},//bezier曲线
Qa{ctx.quadraticCurveTo…this.getNa,4;},//四次曲线
S{ctx.stroke;},//stroke
P{ctx.closePath;},//关闭路径
F{ctx.fill;},//fill
B{ctx.beginPath;},//开始路径
la{//线段
ctx.beginPath;
ctx.moveTo…this.getNa,2;
ctx.lineTo…this.getNa,2;
ctx.stroke;
},
Sta{//集合样式
var style=样式[a.shift];
Object.keystyle.forEachprop=>ctx[prop]=style[prop];
},
}
//获取命令字符串并绘制其中的内容
函数拉伸形状{
var a=形状分割,;
whilea.length>0{
drawFuncs[a.shift]a;
}
}
//创建画布并添加到DOM
var canvas=document.createElementcanvas;
画布宽度=200;
画布高度=200;
var ctx=canvas.getContext2d;
document.body.appendChildcanvas;
//定义命名样式
变量样式={
绿色:{
strokeStyle:006600,
线宽:0.3,
},
黑色:{
strokeStyle:黑色,
填充样式:7BA32D,
线宽:0.8,
}
}
//全部画出来
ctx.clearRect0,0,canvas.width,canvas.height;
画[
圣德格林,B,M,115,80,C,201002001035135,S,
圣布莱克,B,M,55,70,Q,20100115,80,
M、 55,70,Q,40,30115,80,
M、 115,80,Q,80,20130,50,,
M、 115,80,Q,200,40130,50,
M、 115,80,Q,235,60185,85,,
M、 115,80,Q,190115185,85,
M、 115,,
80,Q,180135110115,
M、 115,80,Q,60130110115,S,P,F,
圣德格林,
l、 115,80,65,71,,
l、 115,80127,55,,
l、 115,80175,85,,
l、 115,80110,110,,
].加入,;
表达性语言
Javascript以其表达的灵活性而闻名,并且可以允许大多数其他语言中找不到的编码样式
使用Path2D
代码的第一个想法是使用Path2D对象定义绘图命令。它使用与SVG path命令类似的语法
但是为什么不创建自己的命令列表呢
命名样式
首先,让我们找到所有样式并命名它们
var styles = {
dGreen : {
strokeStyle : "#006600",
lineWidth : 0.3,
},
black : {
strokeStyle : "black",
fillStyle : "#7BA32D",
lineWidth : 0.8,
}
}
现在,您可以使用命名样式,如果您将属性命名为与2D API相同的名称,则设置样式非常容易
function setStyle(style){
Object.keys(style).forEach(prop => ctx[prop] = style[prop]);
},
setStyle(styles.black); // sets the style black
如果你想使用你当时没有想到的属性,你不必编写代码,只需设置属性就可以了
styles.black.lineJoin = "round";
setStyle(styles.black); // sets the style black
自定义命令列表。
对于图形命令,您正在多次执行同一组操作。在SVG中,命令是moveto的单个字符M,后跟x,y坐标
我们也可以这样做。命令将是一个字符串,由分隔,然后拆分为一个数组。您可以根据需要从阵列中切换每个命令
首先是对每个命令都有函数的命令对象。在这种情况下,M代表moveTo,L代表lineTo。它获取用于从中获取坐标的数组
var commands = {
M(array){
ctx.moveTo(array.shift(),array.shift());
},
L(array){
ctx.lineTo(array.shift(),array.shift());
}
}
然后用我们的新命令定义一条路径,移动到10,10,然后移动到100100
var path = "M,10,10,L,100,100";
现在我们只需要解析和解释路径
function drawPath(path){
// split the command string into parts
var commandList = path.split(",");
// while there are commands
while(commandList.length > 0){
// use the next command to index the command
// and call the function it names passing the command list so
// it can get the data it needs
commands[commandList.shift()](commandList);
} // do that until there is nothing on the command list
}
现在,您只需提供命令字符串即可绘制所需内容。因为您可以定义命令,所以可以根据需要创建复杂或简单的命令
稍微复杂一点
下面是我创建的用于绘制图像的命令函数和绘图函数
// define draw commands
var drawFuncs = {
getN(a,count){ return a.splice(0,count); }, // gets values from array
M(a){ ctx.moveTo(...this.getN(a,2)); }, // move to
C(a){ ctx.bezierCurveTo(...this.getN(a,6)); }, // bezier curve
Q(a){ ctx.quadraticCurveTo(...this.getN(a,4)); },// quad curve
S(){ ctx.stroke(); }, // stroke
P(){ ctx.closePath(); }, // close path
F(){ ctx.fill(); }, // fill
B(){ ctx.beginPath(); }, // begin path
l(a) { // line segment
ctx.beginPath();
ctx.moveTo(...this.getN(a,2));
ctx.lineTo(...this.getN(a,2));
ctx.stroke();
},
St(a){ // set style
var style = styles[a.shift()];
Object.keys(style).forEach(prop=>ctx[prop] = style[prop]);
},
}
// Takes command string and draws what is in it
function draw(shape){
var a = shape.split(",");
while(a.length > 0){
drawFuncs[a.shift()](a);
}
}
您可以将该代码放入一个单独的库中,然后在集中精力渲染时将其忘掉
现在,您可以通过自己定制的声明性语言进行渲染
定义样式
// define named styles
var styles = {
dGreen : {
strokeStyle : "#006600",
lineWidth : 0.3,
},
black : {
strokeStyle : "black",
fillStyle : "#7BA32D",
lineWidth : 0.8,
}
}
创建命令列表并绘制
draw([
"St,dGreen,B,M,115,80,C,20,100,200,100,235,135,S",
"St,black,B,M,55,70,Q,20,100,115,80",
"M,55,70,Q,40,30,115,80",
"M,115,80,Q,80,20,130,50",
"M,115,80,Q,200,40,130,50",
"M,115,80,Q,235,60,185,85",
"M,115,80,Q,190,115,185,85",
"M,115,80,Q,180,135,110,115",
"M,115,80,Q,60,130,110,115,S,P,F",
"St,dGreen",
"l,115,80,65,71",
"l,115,80,127,55",
"l,115,80,175,85",
"l,115,80,110,110",
].join(","));
演示
注意:所有代码都是用ES6编写的,需要Babel或类似的工具才能在传统浏览器上工作
//定义绘图命令
var drawFuncs={
getNa,count{return a.0,count;},//从数组中获取值
Ma{ctx.moveTo…this.getNa,2;},//移动到
Ca{ctx.bezier Curveto…this.getNa,6;},//bezier曲线
Qa{ctx.quadraticCurveTo…this.getNa,4;},//四次曲线
S{ctx.stroke;},//stroke
P{ctx.closePath;},//关闭路径
F{ctx.fill;},//fill
B{ctx.beginPath;},//开始路径
la{//线段
ctx.beginPath;
ctx.moveTo…this.getNa,2;
ctx.lineTo…this.getNa,2;
ctx.stroke;
},
Sta{//集合样式
var style=样式[a.shift];
Object.keystyle.forEachprop=>ctx[prop]=style[prop];
},
}
//获取命令字符串并绘制其中的内容
函数拉伸形状{
var a=形状分割,;
whilea.length>0{
drawFuncs[a.shift]a;
}
}
//创建画布并添加到DOM
var canvas=document.createElementcanvas;
画布宽度=200;
画布高度=200;
var ctx=canvas.getContext2d;
document.body.appendChildcanvas;
//定义命名样式
变量样式={
绿色:{
strokeStyle:006600,
线宽:0.3,
},
黑色:{
strokeStyle:黑色,
填充样式:7BA32D,
线宽:0.8,
}
}
//全部画出来
ctx.clearRect0,0,canvas.width,canvas.height;
画[
圣德格林,B,M,115,80,C,201002001035135,S,
圣布莱克,B,M,55,70,Q,20100115,80,
M、 55,70,Q,40,30115,80,
M、 115,80,Q,80,20130,50,,
M、 115,80,Q,200,40130,50,
M、 115,80,Q,235,60185,85,,
M、 115,80,Q,190115185,85,
M、 115,80,Q,180135110115,
M、 115,80,Q,60130110115,S,P,F,
圣德格林,
l、 115,80,65,71,,
l、 115,80127,55,,
l、 115,80175,85,,
l、 115,80110,110,,
].加入,;
尝试在循环中放置类似的代码块,并从数组或类似对象中获取更改的值。应该没那么难。试着在循环中放置类似的代码块,并从数组或类似的数组中获取更改的值。应该没那么难。不,贝金帕斯必须在那里。没有这张纸条,你的答案会更好。但是,笔划后的closePath是无用的,fill已经在内部执行此closePath,它只是上一个MovedPoint的一行。与其叫它draw,不如叫line-不,beginPath必须在那里。你的a 没有这张纸条,nswer会更好。但是,笔划后的closePath是无用的,fill已经在内部执行此closePath,它只是上一个MovedPoint的一行。与其叫它draw,不如叫line-