HTML5 canvas总是必须是一个矩形吗? [英] Does an HTML5 canvas always have to be a rectangle?
问题描述
我对具有任何形状的画布感兴趣的原因是,可以使用贝塞尔曲线来剪切图像,并且具有围绕画布形状的网页流的文本,即剪切出的图像。 p>
需要的是有一个自由形式的div,SVG和HTML5 canvas的可能性。 (应用于SVG,我理解这将等同于Flash符号。)然后你可以想象为形状应用盒子模型(填充,边框和边框),但它不会是一个盒子(它将平行于形状)!
我想这也是可能的,一个形状内的文本可以围绕一个形状流动的文本。
我在这里阅读了一篇关于使用CSS形状创建非矩形布局的有趣博文: http://sarasoueidan.com/blog/css-shapes/
,但不包括在形状内包裹文字。 p>
然后,还有一个用于Brackets(代码编辑器)的CSS Shapes编辑器:
http://blogs.adobe.com/webplatform/2014/04/17/css-shapes-editor-in-brackets /
很简单,它可能听起来实际上涉及到几个步骤来实现。
大纲看起来像这样:
- 将形状定义为多边形,点阵列
- 查找多边形的边界(多边形适合的区域)
- 使用cetronid算法或简单的brute- force
- 定义文本的行高,并将其作为扫描行数的基础
- 填充算法来找到可以填充文本的形状内的段。这样的步骤是:
- 使用奇数/偶数扫描器通过获得与文本扫描线和每行的交叉点(使用线交叉数学)
- 使用奇数和偶数点对点进行排序x
- 使用原始多边形添加剪辑
- 绘制图像
- 使用片段获取宽度。
- 当文字宽度符合片段宽度时,请打印符合 的字符
换句话说:您需要实现多边形填充算法,而不是填充行(每个像素行),您使用该行作为文本的基础。
这是完全可行的;实际上,我为这个问题为自己创造了一个挑战,为了它的乐趣,所以我创建了一个通用
上面描述的原理已经实现,并可视化的步骤:
定义多边形和填充 - 这里我选择使用简单的暴力,并基于中心和填充值计算较小的多边形 - 浅灰色是原始多边形,黑色显然是合同版本:
点被定义为数组 [x1,y1,x2,y2,... xn,yn]
(参见所有这些部分的完整源代码链接):
var pPoints = [],
i = 0,x,y,a,d,dx,dy;
for(; i< points.length; i + = 2){
x = points [i];
y = points [i + 1];
dx = x - bounds.px;
dy = y - bounds.py;
a = Math.atan2(dy,dx);
d = Math.sqrt(dx * dx + dy * dy) - padding;
pPoints.push(bounds.px + d * Math.cos(a),
bounds.py + d * Math.sin(a));
}
下一步是定义我们要扫描的行。行是基于字体的行高:
这很简单 - 只需确保起点和终点在多边形之外。
我们使用奇/偶扫描方法,并检查扫描线与多边形中所有线的交点。
检测相交线的代码是:
function getIntersection(line1,line2){
//unroll对象
var p0x = line1.x1,
p0y = line1.y1,
p1x = line1.x2,
p1y = line1.y2,
p2x = line2.x1,
p2y = line2.y1,
p3x = line2.x2,
p3y = line2.y2,
//坐标之间的差值
d1x = p1x - p0x,
d1y = p1y - p0y,
d2x = p3x - p2x,
d2y = p3y - p2y,
//决定符
d = d1x * d2y - d2x * d1y,
px,py,
s,t;
//如果不相交/是平行的,然后立即返回
if(Math.abs(d)< 1e-14)
return null;
//为交点求解x和y
px = p0x - p2x;
py = p0y - p2y;
s =(d1x * py-d1y * px)/ d;
if(s> = 0& s< = 1){
//如果s在范围内,计算t
t =(d2x * py- d2y * px)/ d;
if(t> = 0& t< = 1){
return {x:p0x +(t * d1x),
y:p0y + t * d1y)}
}
}
return null;
}
然后我们对每一行的点进行排序,并使用点对来创建片段 - 这实际上是一个多边形算法。结果将是:
构建段的代码对于这个帖子有点广泛,所以请查看上面链接的项目。
使用这些段替换为实际文本。我们需要从当前文本指针中扫描一个文本,并查看在段宽度内适合多少。当前的代码有点基本,跳过了很多考虑,如断词,文本基线位置等,但是对于初始使用它会做。
结果放在一起将是:
希望这可以了解所涉及的步骤。
The reason I'm interested in canvases having any shape is that it would then be possible to cut out images with Bezier curves and have the text of a web page flow around the canvas shape, i.e. cut-out image.
What is needed is the possibility to have a free-form shaped div, SVG and HTML5 canvas. (Applied to SVG, I understand this would be equivalent to Flash symbols.) You could then imagine applying a box model (padding, border and margin) for shapes, but it wouldn't be a box (it would be parallel to the shape)!
I suppose it would also then be possible to have text that wraps inside a shape as much as text that flows around a shape.
I read an interesting blog post about "Creating Non-Rectangular Layouts with CSS Shapes" here: http://sarasoueidan.com/blog/css-shapes/
but it doesn't include text wrapping inside a shape.
Then, there's also a CSS Shapes editor for Brackets (a code editor): http://blogs.adobe.com/webplatform/2014/04/17/css-shapes-editor-in-brackets/
As simple as it may sound it actually involves quite a few steps to achieve.
An outline would look something like this:
- Define the shape as a polygon, ie. point array
- Find bounds of polygon (the region the polygon fits inside)
- Contract polygon with padding using either a cetronid algorithm or simply a brute-force approach using center of bounds
- Define line height of text and use that as a basis for number of scan-lines
- Basically use a polygon-fill algorithm to find segment within the shape which can fill in text. The steps for this is:
- Use an odd/even scanner by getting an intersection point (using line intersection math) with text scan line and each of the lines between the points in the polygon
- Sort the points by x
- use odd and even point to create a segment. This segment will always be inside the polygon
- Add clipping using original polygon
- Draw in image
- Use the segments to get a width. Start parsing the text to fill and measure the width.
- When text width fits within the segment width then print the chars that fits
- Repeat for next text/words/chars until end of text or segments
In other words: you would need to implement a polygon fill algorithm but instead of filling in lines (per pixel line) you use the line as basis for the text.
This is fully doable; actually, I went ahead to create a challenge for myself on this problem, for the fun of it, so I created a generic solution that I put on GitHub released under MIT license.
The principle described above are implemented, and to visualize the steps:
Define the polygon and padding - here I chose to just use a simple brute-force and calculate a smaller polygon based on center and a padding value - the light grey is the original polygon and the black obviously the contracted version:
The points are defined as an array [x1, y1, x2, y2, ... xn, yn]
and the code to contract it (see link to project for full source on all these parts):
var pPoints = [],
i = 0, x, y, a, d, dx, dy;
for(; i < points.length; i += 2) {
x = points[i];
y = points[i+1];
dx = x - bounds.px;
dy = y - bounds.py;
a = Math.atan2(dy, dx);
d = Math.sqrt(dx*dx + dy*dy) - padding;
pPoints.push(bounds.px + d * Math.cos(a),
bounds.py + d * Math.sin(a));
}
Next step is to define the lines we want to scan. The lines are based on line height for font:
That is simple enough - just make sure the start and end points are outside the polygon.
We use an odd/even scan approach and check intersection of the scanline versus all lines in the polygon. If we get a intersect point we store that in a list for that line.
The code to detect intersecting lines is:
function getIntersection(line1, line2) {
// "unroll" the objects
var p0x = line1.x1,
p0y = line1.y1,
p1x = line1.x2,
p1y = line1.y2,
p2x = line2.x1,
p2y = line2.y1,
p3x = line2.x2,
p3y = line2.y2,
// calc difference between the coords
d1x = p1x - p0x,
d1y = p1y - p0y,
d2x = p3x - p2x,
d2y = p3y - p2y,
// determinator
d = d1x * d2y - d2x * d1y,
px, py,
s, t;
// if is not intersecting/is parallel then return immediately
if (Math.abs(d) < 1e-14)
return null;
// solve x and y for intersecting point
px = p0x - p2x;
py = p0y - p2y;
s = (d1x * py - d1y * px) / d;
if (s >= 0 && s <= 1) {
// if s was in range, calc t
t = (d2x * py - d2y * px) / d;
if (t >= 0 && t <= 1) {
return {x: p0x + (t * d1x),
y: p0y + (t * d1y)}
}
}
return null;
}
Then we sort the point for each line and use pairs of points to create segments - this is actually a polygon-fill algorithm. The result will be:
The code to build segments is a bit extensive for this post so check out the project linked above.
And finally we use those segments to replace with actual text. We need to scan a text from current text pointer and see how much will fit inside the segment width. The current code is somewhat basic and skips a lot of considerations such as word breaks, text base-line position and so forth, but for initial use it will do.
The result when put together will be:
Hope this gives an idea about the steps involved.
这篇关于HTML5 canvas总是必须是一个矩形吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!