HTML5画布是否必须是矩形?

时间:2014-04-15 17:14:55

标签: javascript canvas html5-canvas shapes

我对具有任何形状的画布感兴趣的原因是,然后可以用贝塞尔曲线剪切图像并使网页的文本围绕画布形状流动,即切出图像。

我们需要的是拥有自由形状div,SVG和HTML5画布的可能性。 (应用于SVG,我知道这相当于Flash符号。)然后你可以设想为形状应用一个盒子模型(填充,边框和边距),但它不会是一个盒子(它将平行于形状)!

我想那时也可以将文本包裹在形状中,就像在文本周围流动的文本一样。

我读了一篇有趣的博客文章,关于"使用CSS形状创建非矩形布局"在这里:http://sarasoueidan.com/blog/css-shapes/

但它不包含文字包装内的形状。

然后,还有一个用于Brackets的CSS Shapes编辑器(代码编辑器): http://blogs.adobe.com/webplatform/2014/04/17/css-shapes-editor-in-brackets/

1 个答案:

答案 0 :(得分:5)

听起来很简单,实际上需要完成很多步骤。

大纲看起来像这样:

  • 将形状定义为多边形,即。点阵
  • 查找多边形的边界(多边形适合的区域)
  • 使用cetronid算法进行填充的合约多边形或使用边界中心的蛮力方法
  • 定义文本的行高,并将其用作扫描行数的基础
  • 基本上使用多边形填充算法来查找可以填充文本的形状内的线段。这个步骤是:
    • 通过使用文本扫描线和多边形中的点之间的每条线获得交叉点(使用线交叉数学)来使用奇数/偶数扫描仪
    • 按x
    • 对点进行排序
    • 使用奇数和偶数点创建一个段。此段将始终位于多边形
  • 使用原始多边形添加剪辑
  • 绘制图像
  • 使用线段获得宽度。开始解析文本以填充和测量宽度。
  • 当文字宽度适合片段宽度时,则打印适合
  • 的字符
  • 重复下一个文字/字词/字符,直到文字或片段结尾

换句话说:您需要实现多边形填充算法,但不是填充行(每个像素行),而是使用该行作为文本的基础。

这是完全可行的;实际上,我继续为这个问题为自己创造了一个挑战,为了它的乐趣,所以我创建了一个在MIT许可下发布的generic solution that I put on GitHub

实施上述原则,并可视化步骤:

定义多边形和填充 - 在这里我选择只使用一个简单的蛮力,并根据中心和填充值计算一个较小的多边形 - 浅灰色是原始多边形,黑色显然是缩小版本:

Polygon

这些点被定义为数组[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));
}

下一步是定义我们要扫描的行。这些行基于字体的行高:

Scanlines

这很简单 - 只需确保起点和终点在多边形之外。

我们使用奇数/偶数扫描方法并检查扫描线与多边形中所有线的交点。如果我们得到一个交叉点,我们将其存储在该行的列表中。

检测相交线的代码是:

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;
}

Intersections

然后我们对每一行的点进行排序并使用点对来创建段 - 这实际上是一个多边形填充算法。结果将是:

Segments

构建细分的代码对于这篇文章来说有点广泛,所以请查看上面链接的项目。

最后我们使用这些段来替换实际文本。我们需要从当前文本指针扫描文本,看看在段宽度内适合多少。当前的代码有些基本,并且省略了许多注意事项,例如分词,文本基线位置等等,但是对于初始使用它会有所作为。

汇总后的结果将是:

Result

希望这能让我们了解所涉及的步骤。