2011-01-29 4 views
13

Je dois créer un gradient en plusieurs étapes le long d'une trajectoire circulaire, comme le montre l'image suivante:Création gradient brosse le long d'une trajectoire circulaire

Wheel Gradient

Est-ce que quelqu'un a des idées sur la façon dont cela pourrait être accompli en XAML plutôt que du code? Serait-il possible d'utiliser les balais de dégradé existants ou de les composer d'une manière ou d'une autre pour obtenir cet effet?

+0

Pourquoi les choix de couleurs? Ce n'est pas cohérent avec la cartographie de couleur de ton que j'ai vue auparavant, et les synesthésiques [ont tendance à percevoir cela différemment] (http://rhythmiclight.com/archives/ideas/colorscales.html) de toute façon. Vous ne semblez pas non plus correspondre exactement au [Color Piano Project] (http://colorpiano.com/). –

+0

Les choix de couleurs sont arbitraires et choisis uniquement pour des raisons esthétiques. Il n'y a pas de corrélation avec le ton. – Charlie

Répondre

15

Vous pouvez obtenir un effet transversal en utilisant une transformation non affine telle qu'une transformation en perspective. J'utilisé les idées dans cet article par Charles Petzold:

pour créer une XAML seule région annulaire avec un gradient en coupe radiale. Voici le balisage:

<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> 

et voici le résultat visuel:

enter image description here

L'effet utilise une collection de source de données avec des éléments qui ont deux propriétés, un angle et une brosse. Il dessine quatre quadrants (en haut, à droite, en bas et à gauche) en utilisant un pinceau différent pour chaque quadrant. Ensuite, le tout est coupé dans la région annulaire avec un masque d'opacité.

+0

Cela me semble très bien, mais à quoi ressemble la classe Tuple - en y ajoutant juste quelques propriétés et ma balise lance des erreurs comme la classe "Tuple" ne supporte pas le contenu direct, etc. – Charlie

+0

J'ai fini par mettre en place les Tuples dans le code plutôt que dans le balisage (toujours curieux de savoir à quoi ressemble cette classe), et cela semble être juste ce dont j'ai besoin. Merci pour une solution vraiment intelligente, Rick. :) – Charlie

+0

Je souhaite qu'il y avait une classe standard comme mon conteneur d'utilitaire 'Tuple' juste pour organiser des données d'échantillon sans avoir à définir des classes. Vous pouvez utiliser un tableau x: Array de x: Arrays ou vos propres structures de données. Je vais mettre à jour l'exemple pour utiliser l'ancienne méthode. –

0

Jetez un oeil à Shazzam Vous pourriez écrire un pixelhader qui rend ce dégradé.

Je pense qu'à long terme, cela sera plus facile que de combiner des gradients linéaires. Une autre option consiste simplement à dessiner un bitmap et à l'utiliser.

+0

Merci pour la suggestion, mais un pixel shader semble être trop compliqué pour ce scénario - pourquoi écrire C/HLSL alors que je pourrais juste écrire du code C# pour accomplir cela plus directement? Mais mon véritable objectif était de voir si cela pouvait être fait en XAML. – Charlie

+0

Comme vous pouvez le voir (la réponse de Rick Sladkey), cela peut être fait cependant, je pense que sa solution (les transformations 3D) sera beaucoup plus lente et le code de pixelhader sera beaucoup plus compact. –

1

Dans GDI +/Winforms vous pouvez utiliser le PathGradientBrush pour ce faire:

http://www.bobpowell.net/pgb.htm

http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.pathgradientbrush.aspx

Malheureusement, il n'y a pas de soutien à un PathGradientBrush dans WPF, mais quelques personnes ont demandé ici :

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

(peut-être la peine Castin En raison du manque de support que vous ne pouvez pas faire directement en XAML, vous pouvez cependant utiliser le code GDI + pour créer une image, puis utiliser l'image dans votre XAML. Cela peut vous donner de meilleures performances que d'utiliser une transformation non affine.

+0

Essayez [GradientPath de Charles Petzold] (http://www.charlespetzold.com/blog/2009/02/Graphical-Paths-with-Gradient-Colors.html). – xmedeko

Questions connexes