我正在寻找构建一个绘图应用程序,并且已经能够做常规线和虚线,但我想要一条“波浪”线,也称为蛇形线。关于如何在 AS3 中实现这一点的任何想法?
更新:回复的代码确实回答了这个问题,但希望更多的是徒手蛇,所以在鼠标移动时它可以画出类似下面的东西(尽管它不需要穿过波浪的线)。
我正在寻找构建一个绘图应用程序,并且已经能够做常规线和虚线,但我想要一条“波浪”线,也称为蛇形线。关于如何在 AS3 中实现这一点的任何想法?
更新:回复的代码确实回答了这个问题,但希望更多的是徒手蛇,所以在鼠标移动时它可以画出类似下面的东西(尽管它不需要穿过波浪的线)。
您可以使用贝泽曲线来做到这一点,但最简单的方法可能是绘制线段。这是一个工作示例:
package
{
import flash.display.*;
import flash.events.Event;
import flash.geom.*;
public class Main extends Sprite {
public function Main():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
this.graphics.lineStyle(2, 0x0, 0.5);
// step = 1 creates a high-quality wave
drawSerpentLine(this.graphics, new Point(50, 50), new Point(150, 250), 4, 20, 1);
// step = 8 and the wave is not as smooth
drawSerpentLine(this.graphics, new Point(300, 30), new Point(233, 150), 6, 10, 8);
}
// takes a graphics reference, and draws a serpent line between the two
// specified points using the g's current lineStyle
// frequency: determines how many waves
// amplitude: how "much" wave
// step: the quality (lower means smoother lines at the cost of speed)
private function drawSerpentLine(g : Graphics, from : Point, to : Point, frequency : int = 5, amplitude : int = 20, step : int = 2):void {
// the angle between the two points
var ang : Number = Math.atan2(to.y - from.y, to.x - from.x);
// the distance between the points
var dis : Number = Point.distance(from, to);
// a point which we use to store the current position to draw
var currPoint : Point = new Point(from.x, from.y);
for (var i:int = 0; i <= dis; i += step) {
// how far away (perpendicularly) from the straight lines the current points should be
var waveOffsetLength : Number = Math.sin((i / dis) * Math.PI * frequency) * amplitude;
// calculate the current point.
currPoint.x = from.x + Math.cos(ang) * i + Math.cos(ang - Math.PI/2)*waveOffsetLength;
currPoint.y = from.y + Math.sin(ang) * i + Math.sin(ang - Math.PI/2)*waveOffsetLength;
if (i > 0) {
this.graphics.lineTo(currPoint.x, currPoint.y);
} else {
this.graphics.moveTo(currPoint.x, currPoint.y);
}
}
// close the last line so we end up at the end point
this.graphics.lineTo(to.x, to.y);
}
}
}
更新:
这是一个手绘的修改版本。方法类似:每次鼠标移动,我们都会在两点之间绘制蛇。但是蛇的“阶段”需要存储在一个变量中,以便不同的 drawSerpentLine() 调用在最后一次调用的阶段继续。此外,我们不能只在最后一个鼠标坐标和当前鼠标坐标之间画一条蛇,因为那样不会产生平滑的外观。所以我们平均坐标。
package
{
import flash.display.*;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.*;
public class Main extends Sprite {
private var m_mouseIsDown : Boolean;
private var m_lastPoint : Point = new Point();
private var m_currPoint : Point = new Point();
private var m_phase : Number = 0;
private var m_firstDrawAfterMouseDown : Boolean;
public function Main():void {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void {
this.graphics.lineStyle(2, 0x0, 0.5);
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
this.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseEvent);
this.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
}
private function drawSerpentLine(g : Graphics, from : Point, to : Point, frequency : Number = 0.15, amplitude : int = 6, step : int = 1):void {
// the angle between the two points
var ang : Number = Math.atan2(to.y - from.y, to.x - from.x);
// the distance between the points
var dis : Number = Point.distance(from, to);
for (var i:int = 0; i <= dis; i += step) {
m_phase += frequency;
// how far away (perpendicularly) from the straight lines the current points should be
var waveOffsetLength : Number = Math.sin(m_phase) * amplitude;
// calculate the current point.
var x : Number = from.x + Math.cos(ang) * i + Math.cos(ang - Math.PI/2)*waveOffsetLength;
var y : Number = from.y + Math.sin(ang) * i + Math.sin(ang - Math.PI/2)*waveOffsetLength;
if (m_firstDrawAfterMouseDown) {
this.graphics.moveTo(x, y);
m_firstDrawAfterMouseDown = false;
} else {
this.graphics.lineTo(x, y);
}
}
}
private function onMouseEvent(event : MouseEvent):void {
switch(event.type) {
case MouseEvent.MOUSE_DOWN:
m_mouseIsDown = m_firstDrawAfterMouseDown = true;
m_currPoint.x = m_lastPoint.x = this.mouseX;
m_currPoint.y = m_lastPoint.y = this.mouseY;
break;
case MouseEvent.MOUSE_MOVE:
if (m_mouseIsDown) {
// to create a smoother look we average the mouse coords
// where only 10% of the new mouse position is used, 90% is the old position
// the lower the first percentage the smoother the look, but the more
// the serpent will lag behind the actual mouse position (until you release the mouse)
m_currPoint.x = this.mouseX * 0.1 + m_lastPoint.x * 0.9;
m_currPoint.y = this.mouseY * 0.1 + m_lastPoint.y * 0.9;
drawSerpentLine(this.graphics, m_lastPoint, m_currPoint);
m_lastPoint.x = m_currPoint.x;
m_lastPoint.y = m_currPoint.y;
}
break;
case MouseEvent.MOUSE_UP:
m_mouseIsDown = false;
// when the user releases, we complete the serpent
// by drawing the full distance to the current position
// (no percentages like in mouse move)
m_currPoint.x = this.mouseX;
m_currPoint.y = this.mouseY;
drawSerpentLine(this.graphics, m_lastPoint, m_currPoint);
break;
}
}
}
}