标签:style blog http color io os ar strong for
上一篇说到:ZRender源码分析4:Painter(View层)-中,这次,来补充一下具体的shape
以圆形为例:
document.addEventListener(‘DOMContentLoaded‘, function () {
var canvasDom = document.getElementById(‘canvasId‘),
context = canvasDom.getContext(‘2d‘);
context.lineWidth = 50;
context.arc(100, 100, 50, 0, Math.PI * 2);
context.stroke();
context.lineWidth = 1;
context.moveTo(0,100);
context.lineTo(200,100);
context.stroke();
});
得到的图形如下:
arc方法中,参数分别为x,y,r,startAngle,endAngle,但是经过测量,这个圆形的总宽度不是2r(100),而是150。迷惑了很久,才明白r是圆心到边框中央的长度,而lineWidth比较小的时候,是看不出这种差别的。 如果要获得热区的宽度,那就是2 * r+ lineWidth/2 + lineWidth / 2
,也就是 2 * r + lineWidth
。而热区的最左端就是 x-r-lineWidth / 2
,最上端就是 y-r-lineWidth / 2
。这就解释了在zrender.shape.Circle类中的getRect方法。
getRect : function (style) {
if (style.__rect) {
return style.__rect;
}
var lineWidth;
if (style.brushType == ‘stroke‘ || style.brushType == ‘fill‘) {
lineWidth = style.lineWidth || 1;
}
else {
lineWidth = 0;
}
style.__rect = {
x : Math.round(style.x - style.r - lineWidth / 2),
y : Math.round(style.y - style.r - lineWidth / 2),
width : style.r * 2 + lineWidth,
height : style.r * 2 + lineWidth
};
return style.__rect;
}
先判断传入的style中是否有__rect这个属性,如果有直接返回,缓存,免得进行多次计算。如果brushType为stroke或者fill,确保有lineWidth,默认为1。最后根据上述算法计算出热点区域。其他图形关于lineWidth的计算都跟这个很相似,以后就不再赘述了。
主要看下圆角矩形的画法:
_buildRadiusPath: function(ctx, style) {
//左上、右上、右下、左下角的半径依次为r1、r2、r3、r4
//r缩写为1 相当于 [1, 1, 1, 1]
//r缩写为[1] 相当于 [1, 1, 1, 1]
//r缩写为[1, 2] 相当于 [1, 2, 1, 2]
//r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2]
var x = style.x;
var y = style.y;
var width = style.width;
var height = style.height;
var r = style.radius;
var r1;
var r2;
var r3;
var r4;
if(typeof r === ‘number‘) {
r1 = r2 = r3 = r4 = r;
}
else if(r instanceof Array) {
if (r.length === 1) {
r1 = r2 = r3 = r4 = r[0];
}
else if(r.length === 2) {
r1 = r3 = r[0];
r2 = r4 = r[1];
}
else if(r.length === 3) {
r1 = r[0];
r2 = r4 = r[1];
r3 = r[2];
} else {
r1 = r[0];
r2 = r[1];
r3 = r[2];
r4 = r[3];
}
} else {
r1 = r2 = r3 = r4 = 0;
}
ctx.moveTo(x + r1, y);
ctx.lineTo(x + width - r2, y);
r2 !== 0 && ctx.quadraticCurveTo(
x + width, y, x + width, y + r2
);
ctx.lineTo(x + width, y + height - r3);
r3 !== 0 && ctx.quadraticCurveTo(
x + width, y + height, x + width - r3, y + height
);
ctx.lineTo(x + r4, y + height);
r4 !== 0 && ctx.quadraticCurveTo(
x, y + height, x, y + height - r4
);
ctx.lineTo(x, y + r1);
r1 !== 0 && ctx.quadraticCurveTo(x, y, x + r1, y);
},
// 矩形
var RectangleShape = require(‘zrender/shape/Rectangle‘);
zr.addShape(new RectangleShape({
style : {
x : 100,
y : 100,
width : 100,
height : 50,
color : ‘rgba(135, 206, 250, 0.8)‘,
text:‘rectangle‘,
textPosition:‘inside‘,
radius: [1,2,3,4]
},
draggable : true
}));
zr.render();
椭圆的画法有多种,请看这里 在HTML5的Canvas上绘制椭圆的几种方法,zrender用的是三次贝塞尔曲线法二
如果是实线(solid),直接moveTo,lineTo就搞定了,那虚线怎么画呢?看这里: HTML5 Canvas自定义圆角矩形与虚线(Rounded Rectangle and Dash Line)。zrender中将线的类型分为3种,solid(默认),dashed(虚线),dotted(点线)。 其实虚线和点线性质是一样的,只是线长不一样罢了。
// zrender.shape.Line
buildPath : function(ctx, style) {
if (!style.lineType || style.lineType == ‘solid‘) {
//默认为实线
ctx.moveTo(style.xStart, style.yStart);
ctx.lineTo(style.xEnd, style.yEnd);
}
else if (style.lineType == ‘dashed‘
|| style.lineType == ‘dotted‘
) {
var dashLength =(style.lineWidth || 1)
* (style.lineType == ‘dashed‘ ? 5 : 1);
dashedLineTo(
ctx,
style.xStart, style.yStart,
style.xEnd, style.yEnd,
dashLength
);
}
}
// zrender.util.dashedLineTo
/**
* 虚线lineTo
*/
return function (ctx, x1, y1, x2, y2, dashLength) {
dashLength = typeof dashLength != ‘number‘
? 5
: dashLength;
var deltaX = x2 - x1;
var deltaY = y2 - y1;
var numDashes = Math.floor(
Math.sqrt(deltaX * deltaX + deltaY * deltaY) / dashLength
);
for (var i = 0; i < numDashes; ++i) {
ctx[i % 2 ? ‘lineTo‘ : ‘moveTo‘](
x1 + (deltaX / numDashes) * i,
y1 + (deltaY / numDashes) * i
);
}
ctx.lineTo(x2, y2);
};
可以看到,dashed和dotted的区别就只有一个dashLength(5或者1,不太灵活吧,不能自定义哦),实现思路也很明确:先计算出线的长度(勾股定理),然后计算一共分为多少段,最后用moveTo和lineTo一直画,就行了。
brush : function(ctx, isHighlight, refresh) {
var style = this.style || {};
if (isHighlight) {
// 根据style扩展默认高亮样式
style = this.getHighlightStyle(
style, this.highlightStyle || {}
);
}
var image = style.image;
var me = this;
if (typeof(image) === ‘string‘) {
var src = image;
if (_cache[src]) {
image = _cache[src];
}
else {
image = new Image();//document.createElement(‘image‘);
image.onload = function(){
image.onload = null;
clearTimeout( _refreshTimeout );
_needsRefresh.push( me );
// 防止因为缓存短时间内触发多次onload事件
_refreshTimeout = setTimeout(function(){
refresh && refresh( _needsRefresh );
// 清空needsRefresh
_needsRefresh = [];
}, 10);
};
_cache[ src ] = image;
image.src = src;
}
}
if (image) {
//图片已经加载完成
if (window.ActiveXObject) {
if (image.readyState != ‘complete‘) {
return;
}
}
else {
if (!image.complete) {
return;
}
}
ctx.save();
this.setContext(ctx, style);
// 设置transform
this.updateTransform(ctx);
var width = style.width || image.width;
var height = style.height || image.height;
var x = style.x;
var y = style.y;
if (style.sWidth && style.sHeight) {
var sx = style.sx || 0;
var sy = style.sy || 0;
ctx.drawImage(
image,
sx, sy, style.sWidth, style.sHeight,
x, y, width, height
);
}
else if (style.sx && style.sy) {
var sx = style.sx;
var sy = style.sy;
var sWidth = width - sx;
var sHeight = height - sy;
ctx.drawImage(
image,
sx, sy, sWidth, sHeight,
x, y, width, height
);
}
else {
ctx.drawImage(image, x, y, width, height);
}
// 如果没设置宽和高的话自动根据图片宽高设置
style.width = width;
style.height = height;
this.style.width = width;
this.style.height = height;
if (style.text) {
this.drawText(ctx, style, this.style);
}
ctx.restore();
}
},
/**
* 创建路径,用于判断hover时调用isPointInPath~
* @param {Context2D} ctx Canvas 2D上下文
* @param {Object} style 样式
*/
buildPath : function(ctx, style) {
ctx.rect(style.x, style.y, style.width, style.height);
return;
},
先看getRect:
/**
* 返回矩形区域,用于局部刷新和文字定位
* @param {Object} style
*/
getRect : function(style) {
if (style.__rect) {
return style.__rect;
}
var width = area.getTextWidth(style.text, style.textFont);
var height = area.getTextHeight(style.text, style.textFont);
var textX = style.x; //默认start == left
if (style.textAlign == ‘end‘ || style.textAlign == ‘right‘) {
textX -= width;
}
else if (style.textAlign == ‘center‘) {
textX -= (width / 2);
}
var textY;
if (style.textBaseline == ‘top‘) {
textY = style.y;
}
else if (style.textBaseline == ‘bottom‘) {
textY = style.y - height;
}
else {
// middle
textY = style.y - height / 2;
}
style.__rect = {
x : textX,
y : textY,
width : width,
height : height
};
return style.__rect;
}
为了更好地理解,进行如下测试
zr.addShape(new LineShape(
{
style:
{
xStart: 0,
yStart: 100,
xEnd: 300,
yEnd: 100,
strokeColor: ‘black‘,
lineWidth: 1
}
}));
zr.addShape(new LineShape(
{
style:
{
xStart: 100,
yStart: 0,
xEnd: 100,
yEnd: 300,
strokeColor: ‘black‘,
lineWidth: 1
}
}));
zr.addShape(new TextShape(
{
style:
{
x: 100,
y: 100,
color: ‘red‘,
text: ‘Align:right;\nBaseline:bottom‘,
textAlign: ‘right‘,
textBaseline: ‘bottom‘
},
hoverable: true,
zlevel: 2
}));
zr.addShape(new TextShape(
{
style:
{
x: 100,
y: 100,
color: ‘red‘,
text: ‘Align:right;\nBaseline:top‘,
textAlign: ‘right‘,
textBaseline: ‘top‘
},
hoverable: true,
zlevel: 2
}));
zr.addShape(new TextShape(
{
style:
{
x: 100,
y: 100,
color: ‘red‘,
text: ‘Align:left;\nBaseline:bottom‘,
textAlign: ‘left‘,
textBaseline: ‘bottom‘
},
hoverable: true,
zlevel: 2
}));
zr.addShape(new TextShape(
{
style:
{
x: 100,
y: 100,
color: ‘red‘,
text: ‘Align:left;\nBaseline:top‘,
textAlign: ‘left‘,
textBaseline: ‘top‘
},
hoverable: true,
zlevel: 2
}));
zr.render();
效果如下:
可见,x,y只是一个基准点,并不是左上角的点。所以在getRect中需要重新计算热区。
TextShape依旧覆盖了Base类的brush方法,如下:
brush : function(ctx, isHighlight) {
var style = this.style;
if (isHighlight) {
// 根据style扩展默认高亮样式
style = this.getHighlightStyle(
style, this.highlightStyle || {}
);
}
if (typeof style.text == ‘undefined‘) {
return;
}
ctx.save();
this.setContext(ctx, style);
// 设置transform
this.updateTransform(ctx);
if (style.textFont) {
ctx.font = style.textFont;
}
ctx.textAlign = style.textAlign || ‘start‘;
ctx.textBaseline = style.textBaseline || ‘middle‘;
var text = (style.text + ‘‘).split(‘\n‘);
var lineHeight = area.getTextHeight(‘国‘, style.textFont);
var rect = this.getRect(style);
var x = style.x;
var y;
if (style.textBaseline == ‘top‘) {
y = rect.y;
}
else if (style.textBaseline == ‘bottom‘) {
y = rect.y + lineHeight;
}
else {
y = rect.y + lineHeight / 2;
}
for (var i = 0, l = text.length; i < l; i++) {
if (style.maxWidth) {
switch (style.brushType) {
case ‘fill‘:
ctx.fillText(
text[i],
x, y, style.maxWidth
);
break;
case ‘stroke‘:
ctx.strokeText(
text[i],
x, y, style.maxWidth
);
break;
case ‘both‘:
ctx.fillText(
text[i],
x, y, style.maxWidth
);
ctx.strokeText(
text[i],
x, y, style.maxWidth
);
break;
default:
ctx.fillText(
text[i],
x, y, style.maxWidth
);
}
}
else{
switch (style.brushType) {
case ‘fill‘:
ctx.fillText(text[i], x, y);
break;
case ‘stroke‘:
ctx.strokeText(text[i], x, y);
break;
case ‘both‘:
ctx.fillText(text[i], x, y);
ctx.strokeText(text[i], x, y);
break;
default:
ctx.fillText(text[i], x, y);
}
}
y += lineHeight;
}
ctx.restore();
return;
},
buildPath : function(ctx, style) {
// 非零环绕填充优化
ctx.arc(style.x, style.y, style.r, 0, Math.PI * 2, false);
ctx.moveTo(style.x + style.r0, style.y);
ctx.arc(style.x, style.y, style.r0, 0, Math.PI * 2, true);
return;
},
请参考如下3个链接,太不常用了,不细细分析了。
剩余折线,多边形,正多边形,路径,扇形,五角星,内外旋轮曲线,下次再说。
标签:style blog http color io os ar strong for
原文地址:http://www.cnblogs.com/hhstuhacker/p/zrender-source-painter-shape.html