Html5 Canvas Bezier上的虚线曲线 [英] Dashed Curves on Html5 Canvas Bezier

查看:161
本文介绍了Html5 Canvas Bezier上的虚线曲线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于我的一个应用程序,我需要在Html5画布上的bezier路径上绘制一条虚线曲线......短划线之间的长度和间距应该是可变的......它在JavaFx中是不可能的,查看这个链接 ...我想用Html5 canvas实现同样的效果。我知道如何绘制虚直线,但不能沿着贝塞尔曲线... ...

虽然我不是专家,但我知道贝塞尔绘图算法,我用这个算法看到的问题是,它允许您使用时间参数,范围从0到1 ...



这是不够的,因为绘制虚线bezier,我需要绘制许多小bezier,具有指定的长度参数并且在主贝塞尔路径上的给定间隙距离处。必须有一些JavaFx使用的算法。如果有人能帮助我,那就太好了。

解决方案

我会假定JavaFX使用通用技术绘制任何虚线在这个例子中,恰好在bezier上使用它。



最难的部分是确定每个短划线的起点和终点,这需要知道弧长的贝塞尔曲线在它的各个点上。

有一种分析方法,但我会建议如下:

  var bezier = function(controlPoints,t) {
/ *你的代码在这里,我假设它返回一个x和y的2元素数组。 * /
};

//找出每个短划线中所有点的坐标,不要画出来。
//返回一个数组数组,每个子数组将有偶数个nu-
// merical元素,即x和y对。

//参数dashPattern应该是交替的破折号和空格数组
//长度,例如[10,10]是点,[30,10]是破折号,
// [30,10,10,10]将是30长短划线,10长短空格,10长短划线
//和10长短空格。
var calculateDashedBezier = function(controlPoints,dashPattern){
var step = 0.001; //这实际上应该由智能方法
//设置,而不是使用常量,但它可以作为
//示例。

//可能无偿的辅助函数
var delta = function(p0,p1){
return [p1 [0] - p0 [0],p1 [1] - p0 [1]];
};
var arcLength = function(p0,p1){
var d = delta(p0,p1);
return Math.sqrt(d [0] * d [0] + d [1] * d [1]);
};

var subPaths = [];
var loc = bezier(controlPoints,0);
var lastLoc = loc;

var dashIndex = 0;
var length = 0;
var thisPath = [];
for(var t = step; t <= 1; t + = step){
loc = bezier(controlPoints,t);
length + = arcLength(lastLoc,loc);
lastLoc = loc;

//检测当我们到达短划线或空格的末尾时
if(length> = dashPattern [dashIndex]){

//如果我们我们需要记录路径。
if(dashIndex%2 == 0)
subPaths.push(thisPath);

//转到模式中的下一个短划线或空格
dashIndex =(dashIndex + 1)%dashPattern.length;

//清除arclength和路径。
thisPath = [];
长度= 0;
}

//如果我们在破折号而不是空格中,请在路径中添加一个点。
if(dashIndex%2 == 0){
thisPath.push(loc [0],loc [1]);
}
}
if(thisPath.length> 0)
subPaths.push(thisPath);
返回子路径;
};

//获取前一个函数的输出并构建适当的路径
var pathParts = function(ctx,pathParts){
for(var i = 0; i< pathParts .length; i ++){
var part = pathParts [i];
if(part.length> 0)
ctx.moveTo(part [0],part [1]); (var j = 1; j ctx.lineTo(part [2 * j],part [2 * j + 1])的
;
}
}
};

//结合上述两个函数实际绘制一条虚线。
var drawDashedBezier = function(ctx,controlPoints,dashPattern){
var dashes = calculateDashedBezier(controlPoints,dashPattern);
ctx.beginPath();
ctx.strokeStyle = / * ... * /
ctx.lineWidth = / * ... * /
pathParts(ctx,dashes);
ctx.stroke();
};

这种方法的主要问题是其非智能粒度。当步长对于(小)破折号或(大)曲线来说太大时,步长将无法正常工作,并且破折号边界不会完全落在您想要的位置。当步长太小时,最终可能会在彼此相距一个子像素距离的点上执行 lineTo() s,有时会产生AA伪影。过滤掉子像素距离坐标并不困难,但是生成比您真正需要的顶点更低效。使用这种方法还有一个好处:如果您将替换为一个更好的步长,那么我会考虑进一步分析攻击。

> bezier(controlPoints,t)以及其他任何评估为曲线的东西,那么您将绘制虚线的起点! - 同样也会出现前面段落中列出的相同潜在问题。但是对粒度问题的一个非常好的解决方案可以适用于所有'表现良好'的曲线。


For one of my application I would need to draw a dashed curves on the bezier path in Html5 canvas... The dash' length and gaps in between should be variable... It is achivable in JavaFx, see this link... I would like to achieve same effect using Html5 canvas. I know how to draw dashed straight lines, but not curved lines along the bezier...

Though I am not an expert, I know the bezier drawing algorithm, problem I see with this algorithm is, it allows you to identify coordinates on the bezier using the time parameter which ranges from 0 to 1...

This is not sufficient because to draw a dashed bezier, I would need to draw many small beziers, with specified length parameter and at given gap distance, on the main bezier path. There must be some algorithm which is used by JavaFx. If anyone can help me out that would be great.

解决方案

I would presume that JavaFX is using a general technique for drawing any dashed curve and just happens to be using it on a bezier in that example.

The hard part is figuring out where to start and stop each dash, which requires knowing the arc length of your bezier curve at various points along it.

There is an analytic approach, but I would suggest the following:

var bezier = function(controlPoints, t) {
  /* your code here, I'll presume it returns a 2-element array of x and y. */
};

//just figure out the coordinates of all the points in each dash, don't draw.
//returns an array of arrays, each sub-array will have an even number of nu-
//merical elements, to wit, x and y pairs.

//Argument dashPattern should be an array of alternating dash and space
//lengths, e.g., [10, 10] would be dots, [30, 10] would be dashes,
//[30, 10, 10, 10] would be 30-length dash, 10-length spaces, 10-length dash
// and 10-length space.
var calculateDashedBezier = function(controlPoints, dashPattern) {
  var step = 0.001; //this really should be set by an intelligent method,
                    //rather than using a constant, but it serves as an
                    //example.

  //possibly gratuitous helper functions
  var delta = function(p0, p1) {
    return [p1[0] - p0[0], p1[1] - p0[1]];
  };
  var arcLength = function(p0, p1) {
    var d = delta(p0, p1);
    return Math.sqrt(d[0]*d[0] + d[1] * d[1]);
  };

  var subPaths = [];
  var loc = bezier(controlPoints, 0);
  var lastLoc = loc;

  var dashIndex = 0;
  var length = 0;
  var thisPath = [];
  for(var t = step; t <= 1; t += step) {
    loc = bezier(controlPoints, t);
    length += arcLength(lastLoc, loc);
    lastLoc = loc;

    //detect when we come to the end of a dash or space
    if(length >= dashPattern[dashIndex]) {

      //if we are on a dash, we need to record the path.
      if(dashIndex % 2 == 0)
        subPaths.push(thisPath);

      //go to the next dash or space in the pattern
      dashIndex = (dashIndex + 1) % dashPattern.length;

      //clear the arclength and path.
      thisPath = [];
      length = 0;
    }

    //if we are on a dash and not a space, add a point to the path.
    if(dashIndex % 2 == 0) {
      thisPath.push(loc[0], loc[1]);
    }
  }
  if(thisPath.length > 0)
    subPaths.push(thisPath);
  return subPaths;
};

//take output of the previous function and build an appropriate path
var pathParts = function(ctx, pathParts) {
  for(var i = 0; i < pathParts.length; i++) {
    var part = pathParts[i];
    if(part.length > 0)
      ctx.moveTo(part[0], part[1]);
    for(var j = 1; j < part.length / 2; j++) {
      ctx.lineTo(part[2*j], part[2*j+1]);
    }
  }
};

//combine the above two functions to actually draw a dashed curve.
var drawDashedBezier = function(ctx, controlPoints, dashPattern) {
  var dashes = calculateDashedBezier(controlPoints, dashPattern);
  ctx.beginPath();
  ctx.strokeStyle = /* ... */
  ctx.lineWidth = /* ... */
  pathParts(ctx, dashes);
  ctx.stroke();
};

The main problem with this approach is its unintelligent granularity. When step is too big for your (small) dashes or (big) curve, the step size will not work well and dash boundaries will not fall exactly where you want them to. When step is too small, you may end up doing lineTo()s on points that are a sub-pixel distance away from each other, making for AA artifacts sometimes. Filtering out sub-pixel distance coordinates is not hard, but it is inefficient to generate more 'vertices' than you really need. Coming up with a better step size is actually something I'd consider attacking more analytically.

There is one bonus to using this approach: if you replace bezier(controlPoints, t) with anything else that evaluates to a curve, you'll be drawing dashed whatevers!-- again with the same potential problems listed in the previous paragraph. But a really good solution to the granularity problem could work for all 'well-behaved' curves.

这篇关于Html5 Canvas Bezier上的虚线曲线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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