我的应用程序非常“基于连接”,即多个输入/输出。
“电缆”的UI概念正是我所期待的,使用户能够清楚地理解这一概念。 Propellerhead在其音频组件的Reason软件中采用了类似的方法,如this YouTube video (fast forward to 2m:50s)所述。
我可以通过在点A到点B绘制样条来使这个概念在GDI中起作用,必须有一种更优雅的方式在WPF中使用Paths或者其他东西,但是你从哪里开始?当你抓住并摇动它时,有没有一种很好的方法来模拟电缆摆动的动画?
如果已经为WPF发明了这个轮子,我也愿意控制库(商业或开源)。
更新:感谢到目前为止答案中的链接,我几乎就在那里。
我以编程方式创建了BezierCurve
,点1为(0, 0)
,点2为底部“挂起”点,点3为鼠标光标所在的位置。我已经为Point 2创建了一个PointAnimation
,并且应用了ElasticEase
缓动函数来提供“Swinging”效果(即,将中间点反弹一点)。
唯一的问题是,动画似乎运行得有点晚。我每次鼠标移动时都会启动故事板,有没有更好的方法来制作这个动画?到目前为止,我的解决方案位于:
代码:
private Path _path = null;
private BezierSegment _bs = null;
private PathFigure _pFigure = null;
private Storyboard _sb = null;
private PointAnimation _paPoint2 = null;
ElasticEase _eEase = null;
private void cvCanvas_MouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(cvCanvas);
AdjustPath(position.X, position.Y);
}
// basic idea: when mouse moves, call AdjustPath and draw line from (0,0) to mouse position with a "hang" in the middle
private void AdjustPath(double x, double y)
{
if (_path == null)
{
_path = new Path();
_path.Stroke = new SolidColorBrush(Colors.Blue);
_path.StrokeThickness = 2;
cvCanvas.Children.Add(_path);
_bs = new BezierSegment(new Point(0, 0), new Point(0, 0), new Point(0, 0), true);
PathSegmentCollection psCollection = new PathSegmentCollection();
psCollection.Add(_bs);
_pFigure = new PathFigure();
_pFigure.Segments = psCollection;
_pFigure.StartPoint = new Point(0, 0);
PathFigureCollection pfCollection = new PathFigureCollection();
pfCollection.Add(_pFigure);
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures = pfCollection;
_path.Data = pathGeometry;
}
double bottomOfCurveX = ((x / 2));
double bottomOfCurveY = (y + (x * 1.25));
_bs.Point3 = new Point(x, y);
if (_sb == null)
{
_paPoint2 = new PointAnimation();
_paPoint2.From = _bs.Point2;
_paPoint2.To = new Point(bottomOfCurveX, bottomOfCurveY);
_paPoint2.Duration = new Duration(TimeSpan.FromMilliseconds(1000));
_eEase = new ElasticEase();
_paPoint2.EasingFunction = _eEase;
_sb = new Storyboard();
Storyboard.SetTarget(_paPoint2, _path);
Storyboard.SetTargetProperty(_paPoint2, new PropertyPath("Data.Figures[0].Segments[0].Point2"));
_sb.Children.Add(_paPoint2);
_sb.Begin(this);
}
_paPoint2.From = _bs.Point2;
_paPoint2.To = new Point(bottomOfCurveX, bottomOfCurveY);
_sb.Begin(this);
}
答案 0 :(得分:9)
如果你想要真正的动态运动(即,当你“摇动”鼠标指针时,你可以创建沿着绳索传播的波浪),你将需要使用有限元技术。但是,如果你对静态行为没问题,你可以简单地使用贝塞尔曲线。
首先,我将简要介绍有限元方法,然后详细介绍静态方法。
动态方法
将“绳索”分成大量(1000个左右)“元素”,每个元素都有一个位置和速度矢量。使用CompositionTarget.Rendering事件计算每个元素位置,如下所示:
计算沿着相邻元素“cord”的每个元素的拉力,这与元素之间的距离成比例。假设绳索本身无质量。
计算每个“元素”上的净力矢量,该元素由沿着绳索的每个相邻元素的拉力加上恒定的重力组成。
使用质量常数将力矢量转换为加速度,并使用运动方程更新位置和速度。
使用带有BeginFigure后跟PolyLineTo的StreamGeometry构建绘制线条。有这么多点,没有理由进行额外的计算来创建一个三次贝塞尔近似。
静态方法
将你的绳索划分为30个区段,每个区段是一个与接触网y = a cosh(x / a)近似的三次贝塞尔曲线。您的终点控制点应位于悬链线上,平行线应与悬链线相切,控制线长度根据悬链线的二阶导数设置。
在这种情况下,您可能还想渲染StreamGeometry,使用BeginFigure和PolyBezierTo来构建它。
我会将其实现为类似于Rectangle和Ellipse的自定义Shape子类“Catenary”。在这种情况下,您只需覆盖DefiningGeometry属性。为了提高效率,我还会覆盖CacheDefiningGeometry,GetDefiningGeometryBounds和GetNaturalSize。
您首先要决定如何参数化您的悬链线,然后为所有参数添加DependencyProperties。确保在FrameworkPropertyMetadata中设置AffectsMeasure和AffectsRender标志。
一个可能的参数化是XOffset,YOffset,Length。另一个可能是XOffset,YOffset,SagRelativeToWidth。这将取决于最容易绑定的内容。
定义DependencyProperties后,实现DefiningGeometry属性以计算三次贝塞尔曲线控制点,构造StreamGeometry并返回它。
如果这样做,您可以将Catenary控件放在任何位置并获得悬链曲线。
答案 1 :(得分:1)
路径中的用户bezier曲线段。
http://www.c-sharpcorner.com/UploadFile/dbeniwal321/WPFBezier01302009015211AM/WPFBezier.aspx
答案 2 :(得分:1)
恕我直言'悬挂'(物理模拟)电缆是一个过度做的情况 - 有利于看起来可用性。
您确定不仅仅是在混淆用户体验吗?
在基于节点/连接的UI中,我发现 clear 连接(比如在Quartz Composer:http://ellington.tvu.ac.uk/ma/wp-content/uploads/2006/05/images/Quartz%20Composer_screenshot_011.png中)比眼睛更像重要的方式,比如朝不同方向前进的摆动电缆(由于重力下降)比实际连接点的位置。 (同时,在模拟中占用CPU周期,这可能在其他地方更有用)
只需我0.02美元