【问题标题】:Creating Gradient Brush along a Circular Path沿圆形路径创建渐变画笔
【发布时间】:2011-01-29 21:47:45
【问题描述】:

我需要沿圆形路径创建多级渐变,如下图所示:

是否有人对如何在 XAML 而不是代码中实现这一点有任何想法?是否有可能使用现有的渐变画笔或以某种方式将它们合成来达到这种效果?

【问题讨论】:

  • 为什么选择颜色?它与我以前见过的任何基调颜色映射都不一致,而且无论如何联觉tend to perceive this quite differently。您似乎也不完全匹配 Color Piano Project
  • 颜色选择是任意的,仅出于审美原因选择。与音高没有相关性。

标签: c# wpf gradient


【解决方案1】:

您可以通过使用非仿射变换(例如透视变换)来获得交叉径向效果。我使用了 Charles Petzold 的这篇文章中的想法:

创建具有交叉径向渐变的仅限 XAML 的环形区域。这是标记:

<Canvas x:Name="LayoutRoot">
    <Canvas.Resources>
        <x:Array x:Key="sampleData" Type="sys:Object">
            <x:Array Type="sys:Object">
                <sys:Double>0</sys:Double>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                    <GradientStop Color="Red" Offset="0"/>
                    <GradientStop Color="Yellow" Offset="0.5"/>
                    <GradientStop Color="Blue" Offset="1"/>
                </LinearGradientBrush>
            </x:Array>
            <x:Array Type="sys:Object">
                <sys:Double>90</sys:Double>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                    <GradientStop Color="Blue" Offset="0"/>
                    <GradientStop Color="Green" Offset="0.5"/>
                    <GradientStop Color="Red" Offset="1"/>
                </LinearGradientBrush>
            </x:Array>
            <x:Array Type="sys:Object">
                <sys:Double>180</sys:Double>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                    <GradientStop Color="Red" Offset="0"/>
                    <GradientStop Color="Yellow" Offset="0.5"/>
                    <GradientStop Color="Blue" Offset="1"/>
                </LinearGradientBrush>
            </x:Array>
            <x:Array Type="sys:Object">
                <sys:Double>270</sys:Double>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                    <GradientStop Color="Blue" Offset="0"/>
                    <GradientStop Color="Green" Offset="0.5"/>
                    <GradientStop Color="Red" Offset="1"/>
                </LinearGradientBrush>
            </x:Array>
        </x:Array>
    </Canvas.Resources>
    <ItemsControl ItemsSource="{StaticResource sampleData}">
        <ItemsControl.OpacityMask>
            <RadialGradientBrush>
                <GradientStop Color="Transparent" Offset="0.95"/>
                <GradientStop Color="White" Offset="0.949"/>
                <GradientStop Color="White" Offset="0.501"/>
                <GradientStop Color="Transparent" Offset="0.5"/>
            </RadialGradientBrush>
        </ItemsControl.OpacityMask>
        <ItemsControl.Template>
            <ControlTemplate TargetType="ItemsControl">
                <ItemsPresenter/>
            </ControlTemplate>
        </ItemsControl.Template>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Canvas Width="1" Height="1">
                    <Canvas.RenderTransform>
                        <RotateTransform Angle="{Binding [0]}" CenterX="124" CenterY="124"/>
                    </Canvas.RenderTransform>
                    <Viewport3D Width="250" Height="250">
                        <ModelVisual3D>
                            <ModelVisual3D.Content>
                                <Model3DGroup>
                                    <GeometryModel3D>
                                        <GeometryModel3D.Geometry>
                                            <MeshGeometry3D Positions="0 0 0, 0 1 0, 1 0 0, 1 1 0" TextureCoordinates="0 1, 0 0, 1 1, 1 0" TriangleIndices="0 2 1, 2 3 1"/>
                                        </GeometryModel3D.Geometry>
                                        <GeometryModel3D.Material>
                                            <DiffuseMaterial Brush="{Binding [1]}"/>
                                        </GeometryModel3D.Material>
                                        <GeometryModel3D.Transform>
                                            <MatrixTransform3D Matrix="0.002,0,0,0,-0.499,-0.498,0,-0.998,0,0,1,0,0.499,0.5,0,1"/>
                                        </GeometryModel3D.Transform>
                                    </GeometryModel3D>
                                    <AmbientLight Color="White" />
                                </Model3DGroup>
                            </ModelVisual3D.Content>
                        </ModelVisual3D>
                        <Viewport3D.Camera>
                            <OrthographicCamera Position="0.5 0.5 1" LookDirection="0 0 -1" UpDirection="0 1 0" Width="1"/>
                        </Viewport3D.Camera>
                    </Viewport3D>
                </Canvas>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Canvas>

这是视觉效果:

效果使用具有两个属性的数据源集合,一个角度和一个画笔。它为每个象限使用不同的画笔绘制四个象限(上、右、下和左)。然后用不透明蒙版将整个东西剪裁到环形区域。

【讨论】:

  • 这对我来说看起来很不错,但是 Tuple 类看起来如何——只需向它添加一些属性,我的标记就会引发错误,例如“Tuple”类不支持直接内容等.
  • 我最终在代码中而不是在标记中设置元组(仍然对那个类的样子感到好奇),这似乎正是我所需要的。感谢您提供非常聪明的解决方案,瑞克。 :)
  • 我希望有一个像我的Tuple 实用程序容器这样的标准类,仅用于组织示例数据而无需定义类。您可以使用 x:Array 的 x:Array 或您自己的数据结构。我将更新示例以使用前一种方法。
  • 对于任何在制作动画时片段之间的间隙很小的人,我将MeshGeometry3D Positions 更改为“0 0 0, 0 1.05 0, 1.05 0 0, 1.05 1.05 0”,它看起来很棒。
【解决方案2】:

在 GDI+/Winforms 中,您可以使用 PathGradientBrush 来执行此操作:

很遗憾,WPF 中不支持 PathGradientBrush,但有些人在这里提出了要求:

http://dotnet.uservoice.com/forums/40583-wpf-feature-suggestions/suggestions/480949-add-a-pathgradientbrush-like-in-winforms-

(可能也值得投票!)

由于缺乏支持,您无法直接在 XAML 中执行此操作,但是您可以使用 GDI+ 代码创建图像,然后在 XAML 中使用该图像。与使用非仿射变换相比,这可能会给您带来更好的性能。

【讨论】:

【解决方案3】:

看看Shazzam 你可以写一个像素着色器来渲染这个渐变。

我认为从长远来看,这将比组合线性渐变更容易。另一种选择是简单地绘制位图并使用它。

【讨论】:

  • 感谢您的建议,但是对于这种情况,像素着色器似乎有点矫枉过正——当我可以编写一些 C# 代码来更直接地完成此任务时,为什么还要编写 C/HLSL?但我真正的目标是看看它是否可以在 XAML 中完成。
  • 如您所见(Rick Sladkey 的回答),它可以完成,但我确实认为他的解决方案(3D 转换)会慢很多,并且像素着色器的代码会更紧凑。
最近更新 更多