# How to draw an arrow with two colors?

I want to draw arrow with two colors, the resulted arrow should be filled by one color at the top and by other color at the bottom, like the following code do to a rectangle:

\draw[top color=blue,bottom color=green] (0,0) rectangle (2,1);


What should I do?

-
Related Question/Possible Duplicate: Arrow with two colors with TikZ – Peter Grill Oct 11 '13 at 7:15
@percusse I had changed my question to make it clear. – windy Oct 11 '13 at 7:28
@PeterGrill that is an interesting question but with a little difference to mine – windy Oct 11 '13 at 7:29
Could you make your question a bit clear? With arbitrary paths, top and bottom might confuse the reader: is bottom the bottom of the whole path or just the line bottom part? Having an image might be great. – Claudio Fiandrino Oct 11 '13 at 8:12
How the transition between colors should be? If a gradient would suffice, this question might be a good starting point. – Pouya Oct 11 '13 at 9:09

## 3 Answers

Hmm, so I'm the only one who thought the requirement was to shade an arrowhead on a path. Oh well...

Decorations can be used, but are slow. The following is far from perfect, but is basically usable. In the first two versions transformations like scaling and rotation will ruin everything, this is (mostly) corrected in the third version.

The approach involves creating a fading from the required path, which in principle is easy, but to reduce the amount of typing involved I've tried to automate it using a key. Some fooling around has to be done to stop the arrowheads getting cut off.

\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{fadings,decorations.pathmorphing}

\makeatletter
\newif\iftikz@shading@path
\tikzset{
% Need this for the bounding box calculation of the
% fading and the rectangle that bounds the fading.
% If set to zero things (e.g., arrows) may get cut off.
fading sep/.store in=\fadingsep,
fading sep=0.25cm,
shaded path/.code={%
% Prevent this stuff happning recursively.
\iftikz@shading@path%
\else%
\tikz@shading@pathtrue%
\tikz@addmode{%
% Interrupt the picture to create a fading.
\pgfinterruptpicture%
\begin{tikzfadingfrompicture}[name=.]
\path [shade=none,fill=none]#1;%
% Need to set the bounding box manually. Include the \fadingsep border.
\xdef\fadingboundingbox{{\noexpand\pgfpoint{\the\pgf@picminx-\fadingsep}{\the\pgf@picminy-\fadingsep}}%
{\noexpand\pgfpoint{\the\pgf@picmaxx+\fadingsep}{\the\pgf@picmaxy+\fadingsep}}}%
\expandafter\pgfpathrectanglecorners\fadingboundingbox%
\pgfusepath{discard}%
\end{tikzfadingfrompicture}%
\endpgfinterruptpicture%
% Install a rectangle that covers the shaded/faded path.
\expandafter\pgfpathrectanglecorners\fadingboundingbox%
% Make the fading happen.
\def\tikz@path@fading{.}%
\tikz@mode@fade@pathtrue%
\tikz@fade@adjustfalse%
\pgfpointscale{0.5}{\expandafter\pgfpointadd\fadingboundingbox}%
\def\tikz@fade@transform{shift={(\the\pgf@x,\the\pgf@y)}}%
}%
\fi%
}
}
\begin{document}

\begin{tikzpicture}[line cap=round, line join=round]

\path[top color=blue, bottom color=green, line cap=rect, fading sep=1cm,
shaded path={
[draw=transparent!0, line width=1cm, -stealth] (-1.5,3) -- (1.5,3)
}];

\path [left color=red, right color=blue, shaded path={
[draw=transparent!0, ultra thick, -stealth] (2,-1.5) .. controls ++(0,2) and ++(0,-2) .. (-2,1.5)
}];

\path [top color=green, bottom color=yellow,
shaded path={
[draw=transparent!25,   ultra thick, dashed,    stealth-stealth] (-2,-1.5) -- (2,1.5)
}];

\path [left color=orange, right color=purple,
shaded path={
[draw=transparent!0, very thick, loosely dotted, stealth-stealth] (240:2 and 1.5) arc (240:-60:2 and 1.5)
}];

\path [left color=black!5, right color=black,
shaded path={
[draw=transparent!20, very thick, <->,
decoration={zigzag, segment length=0.125cm, amplitude=0.0625cm, pre length=0.25cm, post length=0.25cm},
decorate] (-3, 0) -- (3,0)
}];

\end{tikzpicture}

\end{document}


OK, so here is a version in which the path can be specified in the usual way. A bit more fooling around has to be done to get the bounding boxes right. The result is the same as above.

\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{fadings,decorations.pathmorphing,arrows.meta}

\makeatletter
\newif\iftikz@shading@path

\tikzset{
% There are three circumstances in which the fading sep is needed:
% 1. Arrows which do not update the bounding box (which is most of them).
% 2. Line caps/joins and mitres that extend outside the natural bounding
%    box of the path (these are not calculated by PGF).
% 3. Other reasons that haven't been anticipated.
fading xsep/.store in=\pgfpathfadingxsep,
fading ysep/.store in=\pgfpathfadingysep,
fading sep/.style={fading xsep=#1, fading ysep=#1},
fading sep=0.0cm,
shading path/.code={%
% Prevent this stuff happning recursively.
\iftikz@shading@path%
\else%
\tikz@shading@pathtrue%
% \tikz@addmode installs the modes' (e.g., fill, draw, shade)
% to be applied to the path. It isn't usualy for doing more
% changes to the path's construction.
\tikz@addmode{%
\pgfgetpath\pgf@currentfadingpath%
% Get the boudning box of the current path size including the fading sep
\pgfextract@process\pgf@fadingpath@southwest{\pgfpointadd{\pgfqpoint{\pgf@pathminx}{\pgf@pathminy}}%
{\pgfpoint{-\pgfpathfadingxsep}{-\pgfpathfadingysep}}}%%
\pgfextract@process\pgf@fadingpath@northeast{\pgfpointadd{\pgfqpoint{\pgf@pathmaxx}{\pgf@pathmaxy}}%
{\pgfpoint{\pgfpathfadingxsep}{\pgfpathfadingysep}}}%
% Clear the path
\pgfsetpath\pgfutil@empty%
% Interrupt the path and picture to create a fading.
\pgfinterruptpath%
\pgfinterruptpicture%
\begin{tikzfadingfrompicture}[name=.]
\path [shade=none,fill=none, #1] \pgfextra{%
% Set the softpath. Any transformations in #1 will have no effect.
% This will *not* update the bounding box...
\pgfsetpath\pgf@currentfadingpath%
% ...so it is done manually.
\pgf@fadingpath@southwest
\expandafter\pgf@protocolsizes{\the\pgf@x}{\the\pgf@y}%
\pgf@fadingpath@northeast%
\expandafter\pgf@protocolsizes{\the\pgf@x}{\the\pgf@y}%
};
% Now get the bounding of the picture.
\xdef\pgf@fadingboundingbox@southwest{\noexpand\pgfqpoint{\the\pgf@picminx}{\the\pgf@picminy}}%
\xdef\pgf@fadingboundingbox@northeast{\noexpand\pgfqpoint{\the\pgf@picmaxx}{\the\pgf@picmaxy}}%
%
\end{tikzfadingfrompicture}%
\endpgfinterruptpicture%
\endpgfinterruptpath%
% Install a rectangle that covers the shaded/faded path picture.
\pgfpathrectanglecorners{\pgf@fadingboundingbox@southwest}{\pgf@fadingboundingbox@northeast}%
% Make the fading happen.
\def\tikz@path@fading{.}%
\tikz@mode@fade@pathtrue%
\tikz@fade@adjustfalse%10pt
% Shift the fading to the mid point of the rectangle
\pgfpointscale{0.5}{\pgfpointadd{\pgf@fadingboundingbox@southwest}{\pgf@fadingboundingbox@northeast}}%
\edef\tikz@fade@transform{shift={(\the\pgf@x,\the\pgf@y)}}%
}%
\fi%
}
}

\begin{document}

\begin{tikzpicture}[line cap=round, line join=round]

\path[top color=blue, bottom color=green, line cap=rect, fading ysep=.75cm,
shading path={draw=transparent!0, line width=1cm, -stealth}]
(-1.5cm,3cm) -- (1.5cm, 3cm);

\path [left color=red, right color=blue, shading path={draw=transparent!0, ultra thick, Stealth-Stealth}]
(2,-1.5) .. controls ++(0,2) and ++(0,-2) .. (-2,1.5);

\path [top color=green, bottom color=yellow,
shading path={draw=transparent!25,  ultra thick, dashed,    stealth-stealth}]
(-2,-1.5) -- (2,1.5);

\path [left color=orange, right color=purple,
shading path={draw=transparent!0, very thick, loosely dotted, stealth-stealth}]
(240:2 and 1.5) arc (240:-60:2 and 1.5);

\path [left color=black!5, right color=black,
shading path={draw=transparent!20, very thick, <->,
decoration={zigzag, segment length=0.125cm, amplitude=0.0625cm, pre length=0.25cm, post length=0.25cm},
decorate}]
(-3, 0) -- (3,0);

\end{tikzpicture}

\end{document}


So, version 3. This one enables everything to be specified in the 'usual' way and the shade path key sorts everything else out. Unlike the previous versions this version should take care of transformations like rotation, shifting, scaling and so on. Like the previous versions however, it is basically a colossal hack: all sorts of tortuous coding is used to make things work, and may break in some circumstances. The result is the same as the first picture.

Note also, that it is possible to say draw=white rather than draw=transparent!0.

\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{fadings,decorations.pathmorphing}

\makeatletter
\newif\iftikz@shading@path

\tikzset{
% There are three circumstances in which the fading sep is needed:
% 1. Arrows which do not update the bounding box (which is most of them).
% 2. Line caps/joins and mitres that extend outside the natural bounding
%    box of the path (these are not calculated by PGF).
% 3. Other reasons that haven't been anticipated.
shading xsep/.store in=\tikz@pathshadingxsep,
shading ysep/.store in=\tikz@pathshadingysep,
shading sep/.style={shading xsep=#1, shading ysep=#1},
shading sep=0.0cm,
}

\def\tikz@shadepath#1{%
% \tikz@addmode installs the modes' (e.g., fill, draw, shade)
% to be applied to the path. It isn't usualy for doing more
% changes to the path's construction.
\iftikz@shading@path%
\else%
\tikz@shading@pathtrue%
% Get the current path.
\pgfgetpath\tikz@currentshadingpath%
% Get the shading sep without setting any other keys.
\begingroup%
\pgfsys@beginscope% <- may not be necessary
\tikzset{#1}%
\xdef\tikz@tmp{\noexpand\def\noexpand\tikz@pathshadingxsep{\tikz@pathshadingxsep}%
\noexpand\def\noexpand\tikz@pathshadingysep{\tikz@pathshadingysep}}%
\pgfsys@endscope%
\endgroup
\tikz@tmp%
% Get the boudning box of the current path size including the shading sep
\pgfextract@process\pgf@shadingpath@southwest{\pgfpointadd{\pgfqpoint{\pgf@pathminx}{\pgf@pathminy}}%
{\pgfpoint{-\tikz@pathshadingxsep}{-\tikz@pathshadingysep}}}%%
\pgfextract@process\pgf@shadingpath@northeast{\pgfpointadd{\pgfqpoint{\pgf@pathmaxx}{\pgf@pathmaxy}}%
{\pgfpoint{\tikz@pathshadingxsep}{\tikz@pathshadingysep}}}%
% Clear the path
\pgfsetpath\pgfutil@empty%
% Save the current drawing mode and options.
\let\tikz@options@saved=\tikz@options%
\let\tikz@mode@saved=\tikz@mode%
\let\tikz@options=\pgfutil@empty%
\let\tikz@mode=\pgfutil@empty%
% \tikz@options are processed later on.
\tikz@addoption{%
\pgfinterruptpath%
\pgfinterruptpicture%
\begin{tikzfadingfrompicture}[name=.]
\pgfscope%
\tikzset{shade path/.style=}% Make absolutely sure shade path is not inherited.
\path \pgfextra{%
% Set the softpath. Any transformations,draw=none} in #1 will have no effect.
% This will *not* update the bounding box...
\pgfsetpath\tikz@currentshadingpath%
% ...so it is done manually.
\pgf@shadingpath@southwest
\expandafter\pgf@protocolsizes{\the\pgf@x}{\the\pgf@y}%
\pgf@shadingpath@northeast%
\expandafter\pgf@protocolsizes{\the\pgf@x}{\the\pgf@y}%
% Install the drawing modes and options.
\let\tikz@options=\tikz@options@saved%
\let\tikz@mode=\tikz@mode@saved%
};
% Now get the bounding box of the picture.
\xdef\pgf@shadingboundingbox@southwest{\noexpand\pgfqpoint{\the\pgf@picminx}{\the\pgf@picminy}}%
\xdef\pgf@shadingboundingbox@northeast{\noexpand\pgfqpoint{\the\pgf@picmaxx}{\the\pgf@picmaxy}}%
\endpgfscope
\end{tikzfadingfrompicture}%
\endpgfinterruptpicture%
\endpgfinterruptpath%
% Install a rectangle that covers the shaded/faded path picture.
\pgftransformreset%
\pgfpathrectanglecorners{\pgf@shadingboundingbox@southwest}{\pgf@shadingboundingbox@northeast}%
%
% Reset all modes.
\let\tikz@path@picture=\pgfutil@empty%
\tikz@mode@fillfalse%
\tikz@mode@drawfalse%
\tikz@mode@tipsfalse%
\tikz@mode@doublefalse%
\tikz@mode@clipfalse%
\tikz@mode@boundaryfalse%
\tikz@mode@fade@pathfalse%
\tikz@mode@fade@scopefalse%
% Now install shading options.
\tikzset{#1}%
\tikz@mode%
% Make the fading happen.
\def\tikz@path@fading{.}%
\tikz@mode@fade@pathtrue%
\tikz@fade@adjustfalse%
% Shift the fading to the mid point of the rectangle
\pgfpointscale{0.5}{\pgfpointadd{\pgf@shadingboundingbox@southwest}{\pgf@shadingboundingbox@northeast}}%
\edef\tikz@fade@transform{shift={(\the\pgf@x,\the\pgf@y)}}%
\pgfsetfading{\tikz@path@fading}{\tikz@do@fade@transform}%
\tikz@mode@fade@pathfalse%
}%
\fi%
}
\tikzset{
shade path/.code={%
\tikz@addmode{\tikz@shadepath{#1}}%
}
}

\begin{document}

\begin{tikzpicture}[line cap=round, line join=round]

\path[draw=transparent!0, line width=1cm, line cap=butt, -stealth,
shade path={shading ysep=0.75cm, top color=blue, bottom color=green}]
(-1.5,3) -- (1.5, 3);

\draw [draw=transparent!0, shade path={shading xsep=0.125cm, left color=red, right color=blue},
ultra thick, -stealth]
(2,-1.5) .. controls ++(0,2) and ++(0,-2) .. (-2,1.5);

\path [rotate=90, scale=0.5, draw=transparent!25,   ultra thick, dashed, stealth-stealth,
shade path={top color=green, bottom color=yellow}]
(-1.5,-2) -- (1.5,2);

\path [draw=transparent!0, very thick, loosely dotted, stealth-stealth,
shade path={left color=orange, right color=purple}]
(240:2 and 1.5) arc (240:-60:2 and 1.5);

\path [shade path={left color=black!5, right color=black}, draw=transparent!20, very thick, <->,
decoration={zigzag, segment length=0.125cm, amplitude=0.0625cm, pre length=0.25cm, post length=0.25cm},
decorate,]
(-3, 0) -- (3,0);

\end{tikzpicture}

\end{document}

-
Related question: Tikz: Shading a path without any filling – Qrrbrbirlbel Oct 11 '13 at 18:44
Can’t we use the soft path so that we don’t need to give the path as a value to a key but as usual? – Qrrbrbirlbel Oct 11 '13 at 18:53
@Qrrbrbirlbel, it is (sort of) possible as I show above. The principle problem is that setting the softpath directly does not update the bounding box which is required for the fading definition. This must, therefore be saved before creating the fading and manually adjusted for line width etc. – Mark Wibrow Oct 11 '13 at 19:53
Also there is the question of whether it is logical to separate the fading path specification and the options (i.e., line width, arrows etc) for the fading path (which as far as I can see cannot be specified in the 'usual' way). – Mark Wibrow Oct 11 '13 at 19:56
@MarkWibrow I think this is a more scalable solution to my problem, though I don't quite understand how it works ^^, thanks a lot! – windy Oct 12 '13 at 7:30

Here is a solution using single arrow shape (from the shapes.arrows library).

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.arrows}
\begin{document}
\begin{tikzpicture}
\node[single arrow,top color=blue,bottom color=green]{example};
\end{tikzpicture}
\end{document}


-
@Jake Very good advice! ;-) – Paul Gaborit Oct 12 '13 at 6:21

With Tikz (no pgflibrary), this alternative yields

where any shape can be drawn via coordinates (Arrow is an example here).

\documentclass[]{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\filldraw[top color=blue, bottom color=green]
(0,-0.5)-- (1,-0.5)--(1,-1)--(2,0)--(1,1)--(1,0.5)--(0,0.5)--cycle;
\end{tikzpicture}
\end{document}

-