예전부터, 도대체 왜 WPF에 AngleGradientBrush가 없는지 의문이었습니다.
그래서 이번에 각도 기반 그라데이션 효과를 만들어 보게 되었습니다.
하지만 안타깝게도 WPF의 브러시는 재정의 구현이 불가능했습니다.
그래서 ShaderEffect 클래스를 기반으로 AngleGradientEffect 클래스를 만들게 되었습니다.
AngleGradientEffect 클래스에 대한 픽셀 셰이더는 AngleGradientEffect.fx 파일에 작성했습니다.
샘플 프로그램을 만들면서, 로딩 인디케이터도 함께 만들어 봤습니다.
혹시라도 이런 기능이 필요했던 분들께 도움이 되었으면 좋겠습니다.
ShaderEffect를 이용한 AngleGradientEffect 구현 샘플 프로그램입니다.
26개의 좋아요
아메리카노
11월 4, 2024, 5:30오전
2
이거보니 생각났는데 원형 progress bar도 없어서 찾아서 썼었는데 공유해봅니다
5개의 좋아요
좋은 정보 감사합니다. Shape 클래스를 상속받아 구현한 예시군요~
저도 예전에 그런 기능이 필요해서 만들어 쓰긴 했었는데요,
그냥 대충 Attached Property 하나 정의해서 Ellipse의 StrokeDashArray를 조작하는 방식으로 만들었었습니다.
하나 만들어 놓고 나름 잘 써먹었던 것 같네요~ ㅎㅎ
<Ellipse
Stroke="Green" StrokeThickness="50"
Width="100" Height="100"
local:EllipseProgress.Value="{Binding Value}"/>
public static class EllipseProgress
{
public static double GetValue(Ellipse ellipse) => (double)ellipse.GetValue(ValueProperty);
public static void SetValue(Ellipse obj, double value) => obj.SetValue(ValueProperty, value);
public static readonly DependencyProperty ValueProperty =
DependencyProperty.RegisterAttached("Value", typeof(double), typeof(EllipseProgress), new PropertyMetadata(0d, OnPropertyChanged));
private static readonly DependencyProperty EllipseProgressBindingProperty =
DependencyProperty.RegisterAttached("EllipseProgressBinding", typeof(MultiBinding), typeof(EllipseProgress), new PropertyMetadata(null));
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Ellipse ellipse && !(ellipse.GetValue(EllipseProgressBindingProperty) is MultiBinding))
{
var multiBinding = new MultiBinding { Converter = new Converter() };
multiBinding.Bindings.Add(new Binding { Path = new PropertyPath(ValueProperty), Source = ellipse });
multiBinding.Bindings.Add(new Binding(nameof(ellipse.ActualWidth)) { Source = ellipse });
multiBinding.Bindings.Add(new Binding(nameof(ellipse.ActualHeight)) { Source = ellipse });
multiBinding.Bindings.Add(new Binding(nameof(ellipse.StrokeThickness)) { Source = ellipse });
BindingOperations.SetBinding(ellipse, Shape.StrokeDashArrayProperty, multiBinding);
ellipse.SetValue(EllipseProgressBindingProperty, multiBinding);
}
}
class Converter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length >= 4
&& values[0] != DependencyProperty.UnsetValue
&& values[1] is double width
&& values[2] is double height
&& values[3] is double thickness)
{
width -= thickness;
height -= thickness;
var total = Math.PI * Math.Sqrt((width * width + height * height) / 2) / thickness;
var value = Math.Min(1, Math.Max(0, System.Convert.ToDouble(values[0]) / 100)) * total;
var quarter = total * 0.25;
return new DoubleCollection() { Math.Max(0, value - quarter), Math.Min(total - quarter, total - value), value, total };
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
}
7개의 좋아요
예전에 작성했던 샘플 프로그램에 BlurEffect를 추가해봤는데 생각보다 예뻐서 공유해봅니다. ㅎㅎ
기존 소스코드도 수정해 두었습니다. 필요하신 분은 얼마든지 쓰시면 됩니다~
ShaderEffect를 이용한 AngleGradientEffect 구현 샘플 프로그램입니다.
13개의 좋아요
얼마 전에 사업자 등록을 하면서 사업용 홈페이지를 만들어 보고 있는 요즘, 문득 웹에서는 각도 기반 그라데이션을 어떻게 만드는지 궁금해서 검색해보니 conic-gradient 라는 CSS 함수가 있었습니다.
그러면 SVG에 잘 쓰면 웹에서도 로딩 인디케이터를 만들어 쓸 수 있겠다 싶어서 적용해봤는데, conic-gradient는 SVG 도형에 바로 적용을 못 하더군요. ㅎㅎ
그래서 꼼수로 SVG 내부에 HTML 요소를 포함할 수 있는 foreignObject에 적용하니까 잘 되길래 하나 만들어가지고 코드를 공유해 봅니다.
다음의 코드는 SMIL 애니메이션을 포함한 SVG 이미지 파일을 나타냅니다.
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<filter id="filter-blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="3"/>
</filter>
<mask id="mask-circle">
<circle cx="50%" cy="50%" r="30%" stroke-width="12" stroke="white" fill-opacity="0" stroke-dasharray="130 20" stroke-linecap="round"/>
</mask>
<g filter="url(#filter-blur)">
<foreignObject width="100%" height="100%" mask="url(#mask-circle)" style="background: conic-gradient(rgba(0,255,255,0) 0deg, rgba(0,255,255,0.5) 135deg, rgba(255,255,255,1) 315deg);"/>
<animateTransform attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" dur="1s" repeatCount="indefinite"/>
</g>
</svg>
다음의 SVG 이미지처럼 배경을 체크 무늬로 채운 예시도 하나 만들었습니다.
사용할 때는 다음의 링크처럼 img 태그로 HTML 안에 삽입해서 쓰면 되구요, 아니면 인라인 SVG로도 사용 가능하겠습니다. 크기도 적절하게 설정하면 되구요.. ㅎㅎ
일단 엣지나 크롬에서는 잘 나오는데 다른 브라우저는 어떻게 잘 나올지는 모르겠네요. ㅎㅎ
닷넷 관련 내용은 아니지만 이런 것도 필요하신 분들이 있지는 않을까 해서 공유해봅니다.
그나저나 저는 WPF를 아주 좋아합니다만, SVG도 만만치 않게 좋아지기 시작했습니다. ㅎㅎㅎ
5개의 좋아요