在画布中沿曲线渐变描边 [英] Gradient Stroke Along Curve in Canvas

查看:22
本文介绍了在画布中沿曲线渐变描边的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在画布上绘制一条具有线性渐变描边样式的曲线沿着曲线,如这张图片.在那个页面上有一个链接的 svg 文件,它给出了如何在 svg 中完成效果的说明.也许在画布中可以使用类似的方法?

I'm trying to draw a curve in canvas with a linear gradient stoke style along the curve, as in this image. On that page there is a linked svg file that gives instructions on how to accomplish the effect in svg. Maybe a similar method would be possible in canvas?

推荐答案

A Demo: http://jsfiddle.net/m1erickson/4fX5D/

创建一个沿着路径变化的渐变相当容易:

It's fairly easy to create a gradient that changes along the path:

创建一个沿路径变化的渐变更加困难:

It's more difficult to create a gradient that changes across the path:

要在路径上创建渐变,您需要绘制许多与路径相切的渐变线:

To create a gradient across the path you draw many gradient lines tangent to the path:

如果你画了足够多的切线,那么眼睛看​​到的曲线就是路径上的渐变.

If you draw enough tangent lines then the eye sees the curve as a gradient across the path.

注意:锯齿可能出现在路径梯度的外侧.那是因为渐变实际上是由数百条切线组成的.但是您可以通过使用适当的颜色在渐变的任一侧画一条线来平滑锯齿(这里的抗锯齿线在顶部为红色,在底部为紫色).

Note: Jaggies can occur on the outsides of the path-gradient. That's because the gradient is really made up of hundreds of tangent lines. But you can smooth out the jaggies by drawing a line on either side of the gradient using the appropriate colors (here the anti-jaggy lines are red on the top side and purple on the bottom side).

以下是在路径上创建渐变的步骤:

Here are the steps to creating a gradient across the path:

  • 沿路径绘制数百个点.

  • Plot hundreds of points along the path.

计算路径在这些点的角度.

Calculate the angle of the path at those points.

在每个点上,创建一个线性渐变并在该点的切线上绘制渐变描边线.是的,您必须为每个点创建一个新的渐变,因为线性渐变必须与该点的切线角度相匹配.

At each point, create a linear gradient and draw a gradient stroked line across the tangent of that point. Yes, you will have to create a new gradient for each point because the linear gradient must match the angle of the line tangent to that point.

为了减少因绘制多条单线而造成的锯齿效果,您可以沿着渐变路径的顶部和底部绘制平滑路径以覆盖锯齿.

To reduce the jaggy effect caused by drawing many individual lines, you can draw a smooth path along the top and bottom side of the gradient path to overwrite the jaggies.

这里是注释代码:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>       
<script>
$(function(){

    // canvas related variables
    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    // variables defining a cubic bezier curve
    var PI2=Math.PI*2;
    var s={x:20,y:30};
    var c1={x:200,y:40};
    var c2={x:40,y:200};
    var e={x:270,y:220};

    // an array of points plotted along the bezier curve
    var points=[];

    // we use PI often so put it in a variable
    var PI=Math.PI;

    // plot 400 points along the curve
    // and also calculate the angle of the curve at that point
    for(var t=0;t<=100;t+=0.25){

        var T=t/100;

        // plot a point on the curve
        var pos=getCubicBezierXYatT(s,c1,c2,e,T);

        // calculate the tangent angle of the curve at that point
        var tx = bezierTangent(s.x,c1.x,c2.x,e.x,T);
        var ty = bezierTangent(s.y,c1.y,c2.y,e.y,T);
        var a = Math.atan2(ty, tx)-PI/2;

        // save the x/y position of the point and the tangent angle
        // in the points array
        points.push({
            x:pos.x,
            y:pos.y,
            angle:a
        });

    }


    // Note: increase the lineWidth if 
    // the gradient has noticable gaps 
    ctx.lineWidth=2;

    // draw a gradient-stroked line tangent to each point on the curve
    for(var i=0;i<points.length;i++){

        // calc the topside and bottomside points of the tangent line
        var offX1=points[i].x+20*Math.cos(points[i].angle);
        var offY1=points[i].y+20*Math.sin(points[i].angle);
        var offX2=points[i].x+20*Math.cos(points[i].angle-PI);
        var offY2=points[i].y+20*Math.sin(points[i].angle-PI);

        // create a gradient stretching between 
        // the calculated top & bottom points
        var gradient=ctx.createLinearGradient(offX1,offY1,offX2,offY2);
        gradient.addColorStop(0.00, 'red'); 
        gradient.addColorStop(1/6, 'orange'); 
        gradient.addColorStop(2/6, 'yellow'); 
        gradient.addColorStop(3/6, 'green') 
        gradient.addColorStop(4/6, 'aqua'); 
        gradient.addColorStop(5/6, 'blue'); 
        gradient.addColorStop(1.00, 'purple'); 

        // draw the gradient-stroked line at this point
        ctx.strokeStyle=gradient;
        ctx.beginPath();
        ctx.moveTo(offX1,offY1);
        ctx.lineTo(offX2,offY2);
        ctx.stroke();
    }


    // draw a top stroke to cover jaggies
    // on the top of the gradient curve
    var offX1=points[0].x+20*Math.cos(points[0].angle);
    var offY1=points[0].y+20*Math.sin(points[0].angle);
    ctx.strokeStyle="red";
    // Note: increase the lineWidth if this outside of the
    //       gradient still has jaggies
    ctx.lineWidth=1.5;
    ctx.beginPath();
    ctx.moveTo(offX1,offY1);
    for(var i=1;i<points.length;i++){
        var offX1=points[i].x+20*Math.cos(points[i].angle);
        var offY1=points[i].y+20*Math.sin(points[i].angle);
        ctx.lineTo(offX1,offY1);
    }
    ctx.stroke();


    // draw a bottom stroke to cover jaggies
    // on the bottom of the gradient
    var offX2=points[0].x+20*Math.cos(points[0].angle+PI);
    var offY2=points[0].y+20*Math.sin(points[0].angle+PI);
    ctx.strokeStyle="purple";
    // Note: increase the lineWidth if this outside of the
    //       gradient still has jaggies
    ctx.lineWidth=1.5;
    ctx.beginPath();
    ctx.moveTo(offX2,offY2);
    for(var i=0;i<points.length;i++){
        var offX2=points[i].x+20*Math.cos(points[i].angle+PI);
        var offY2=points[i].y+20*Math.sin(points[i].angle+PI);
        ctx.lineTo(offX2,offY2);
    }
    ctx.stroke();


    //////////////////////////////////////////
    // helper functions
    //////////////////////////////////////////

    // calculate one XY point along Cubic Bezier at interval T
    // (where T==0.00 at the start of the curve and T==1.00 at the end)
    function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
        var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
        var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
        return({x:x,y:y});
    }

    // cubic helper formula at T distance
    function CubicN(T, a,b,c,d) {
        var t2 = T * T;
        var t3 = t2 * T;
        return a + (-a * 3 + T * (3 * a - a * T)) * T
        + (3 * b + T * (-6 * b + b * 3 * T)) * T
        + (c * 3 - c * 3 * T) * t2
        + d * t3;
    }

    // calculate the tangent angle at interval T on the curve
    function bezierTangent(a, b, c, d, t) {
        return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
    };

}); // end $(function(){});
</script>
</head>
<body>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

这篇关于在画布中沿曲线渐变描边的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆