2011-01-29 15 views
13

Ho bisogno di creare un gradiente multi-step lungo un percorso circolare, come dimostrato in questa immagine:Creazione pennello sfumato lungo un percorso circolare

Wheel Gradient

Qualcuno ha qualche idea su come questo potrebbe essere realizzato in XAML piuttosto che in codice? Sarebbe possibile utilizzare i pennelli sfumati esistenti o combinarli in qualche modo per ottenere questo effetto?

+0

Perché le scelte di colore? Non è coerente con nessuna mappatura del colore che ho visto prima, e la sinestesia [tende a percepirlo in modo abbastanza diverso] (http://rhythmiclight.com/archives/ideas/colorscales.html) comunque. Inoltre, non sembra che corrisponda esattamente al [Color Piano Project] (http://colorpiano.com/). –

+0

Le scelte di colore sono arbitrarie e vengono scelte solo per motivi estetici. Non c'è alcuna correlazione al tono. – Charlie

risposta

15

È possibile ottenere un effetto radiale trasversale utilizzando una trasformazione non affine come una trasformazione prospettica. Ho usato le idee in questo articolo da Charles Petzold:

per creare una regione XAML sola anulare con un gradiente trasversale radiale. Ecco il markup:

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

ed ecco il risultato visivo:

enter image description here

L'effetto utilizza una raccolta di origine di dati con gli elementi che hanno due proprietà, un angolo e una spazzola. Disegna quattro quadranti (in alto, a destra, in basso ea sinistra) usando un pennello diverso per ciascun quadrante. Quindi il tutto viene ritagliato nella regione anulare con una maschera di opacità.

+0

Questo mi sembra molto buono, ma come funziona la classe Tuple? Aggiungo solo alcune proprietà e il mio mark-up genera errori come la classe "Tuple" non supporta il contenuto diretto, ecc. – Charlie

+0

Ho finito per impostare le tuple nel codice piuttosto che nel mark-up (ancora curioso di sapere come si presenta quella classe), e questo sembra essere proprio quello di cui ho bisogno. Grazie per una soluzione davvero intelligente, Rick. :) – Charlie

+0

Vorrei che esistesse una classe standard come il mio contenitore di utilità 'Tuple' solo per organizzare i dati di esempio senza dover definire le classi. Puoi usare una x: Array di x: Arrays o le tue strutture di dati. Aggiornerò l'esempio per utilizzare il metodo precedente. –

0

Dai uno sguardo a Shazzam Puoi scrivere un pixelhader che esegue il rendering di questo gradiente.

Penso che a lungo termine sarà più facile che combinare i gradienti lineari. Un'altra opzione è semplicemente disegnare una bitmap e usarla.

+0

Grazie per il suggerimento, ma un pixel shader sembra eccessivo per questo scenario: perché scrivere C/HLSL quando potrei semplicemente scrivere del codice C# per ottenere questo risultato più direttamente? Ma il mio vero obiettivo era vedere se poteva essere fatto in XAML. – Charlie

+0

Come potete vedere (la risposta di Rick Sladkey) può essere fatta comunque, penso che la sua soluzione (le trasformazioni 3D) sarà molto più lenta e il codice per pixelhader sarebbe molto compatto. –

1

in GDI +/WinForms è possibile utilizzare il PathGradientBrush per fare questo:

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

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

purtroppo non c'è il supporto per un PathGradientBrush in WPF, ma poche persone hanno chiesto qui :

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

(forse vale la pena castin

A causa della mancanza di supporto non è possibile farlo direttamente in XAML, è comunque possibile utilizzare il codice GDI + per creare un'immagine e quindi utilizzare l'immagine nel proprio XAML. Questo potrebbe darti prestazioni migliori rispetto all'utilizzo di una trasformazione non affine.

+0

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