原文:https://www.cnblogs.com/LCHL/p/14706407.html
代码下载地址:
https://download.csdn.net/download/qq_43307934/18275748
某些新闻:小米logo换新,程序员一行代码(border-radius:19px)实现,目前此行代码价值200万
某程序员内心:所以还是因为我代码写太少了,所以这200万才没有我的份吗?
这事儿也成功的引起了本羊的注意,花点时间,咱也用WPF来实现一下,到底这超圆角有多好看?
先上效果图:
经过一番了解,其实本质也就一条数学函数:|x|n+|y|n=1,上代码:
public class UIElementHelper
{
public static double GetSuperCornerRadius(FrameworkElement obj)
{
return (double)obj.GetValue(SuperCornerRadiusProperty);
}
public static void SetSuperCornerRadius(FrameworkElement obj, double value)
{
obj.SetValue(SuperCornerRadiusProperty, value);
}
public static readonly DependencyProperty SuperCornerRadiusProperty =
DependencyProperty.RegisterAttached("SuperCornerRadius", typeof(double), typeof(UIElementHelper), new PropertyMetadata(0.0, new PropertyChangedCallback((s, e) =>
{
var n = (double)e.NewValue;
var el = s as FrameworkElement;
if (n == 0)
{
el.Clip = null;
}
else
{
UpdateClip(el);
if (!el.IsLoaded)
{
el.SizeChanged += OnSizeChanged;
}
}
})));
private static void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateClip(sender as FrameworkElement, false);
}
private static void UpdateClip(FrameworkElement el, bool updatePoints = true)
{
if (!updatePoints && el.Clip is StreamGeometry clip && !clip.IsFrozen)
{
//
}
else
{
clip = new StreamGeometry();
var points = GetSuperCornerRadiusPoints(GetSuperCornerRadius(el));
using(var context = clip.Open())
{
context.BeginFigure(points[0], true, true);
context.PolyBezierTo(points, true, true);//根据坐标点画贝塞尔曲线
}
points.Clear();
el.Clip = clip;//元素的剪切路径
}
var group = new TransformGroup();
group.Children.Add(new TranslateTransform(1, 1));//WPF以左上角为坐标原点,所以这里进行位移
var w = el.RenderSize.Width / 2;
group.Children.Add(new ScaleTransform(w, w));//放大
clip.Transform = group;
}
/// <summary>
/// 计算超圆角路径的坐标点
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
public static List<Point> GetSuperCornerRadiusPoints(double n = 3)
{
var points = new List<Point>();
//求出Y>0时坐标
for (double x = -1; x <= 1; x += 0.001)
{
var y = Math.Pow(1 - Math.Pow(Math.Abs(x), n), 1 / n);//公式:|x|^n+|y|^n=1
points.Add(new Point(x, Math.Round(y, 3)));//3位小数,精度应该足够了
}
//Y<0的坐标
var count = points.Count;
for (int i = 1; i < count + 1; i++)
{
var p = points[count - i];
points.Add(new Point(p.X, p.Y * -1));
}
return points;
}
}