/
styles.html
284 lines (269 loc) · 17.6 KB
/
styles.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="generator" content="pandoc">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title></title>
<style type="text/css">code{white-space: pre;}</style>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
<style type="text/css">
body {
font-family: Verdana, Geneva, sans-serif;
margin: 0 2em;
color: #eeeeee;
background-color: #222222;
}
pre {
margin: 0 2em;
padding: 1em;
background-color: #000000;
}
a:visited {
color: #999999;
}
a {
color: #999999;
}
b {
font-weight: bold;
color: #338833;
}
p code, li code {
color: #449944;
font-size: 1.2em;
}
h3 {
margin-top: 3em;
}
img {
margin: 0 2em;
}
li {
line-height: 1.5em;
}
</style>
</head>
<body>
<p><a href="http://gifloopcoder.com">Home</a></p>
<h1 id="gif-loop-coder-glc-documentation">GIF Loop Coder (GLC) Documentation</h1>
<p>Table of Contents</p>
<ol type="1">
<li><a href="intro.html">Introduction to GIF Loop Coder (GLC)</a></li>
<li><a href="objects.html">Objects</a></li>
<li>Styles</li>
</ol>
<ul>
<li><a href="#styles">Styles</a></li>
<li><a href="#color">The Color Module</a></li>
<li><a href="#gradients">Gradients</a></li>
</ul>
<ol start="4" type="1">
<li><a href="properties.html">Property Types</a></li>
<li><a href="tips.html">Tips and Advanced Use</a></li>
</ol>
<h2 id="styles"><a name="styles"></a>Styles</h2>
<p>The following properties can be set on any object when creating it. This also lists their default values if they are not set.</p>
<pre><code>lineWidth 5
strokeStyle "#000000"
fillStyle "#000000"
lineCap "round"
lineJoin "miter"
lineDash []
miterLimit 10
shadowColor null
shadowOffsetX 0
shadowOffsetY 0
shadowBlur 0
globalAlpha 1
translationX 0
translationY 0
shake 0
blendMode "source-over"</code></pre>
<p>All of these defaults are defined on the <code>glc.styles</code> property. So you can change any of these globally by writing, for example:</p>
<pre><code>glc.styles.lineWidth = 20;
list.addCircle({
stroke: true,
fill: false,
radius: [50, 80]
});</code></pre>
<figure>
<img src="images/3.1.gif" alt="" />
</figure>
<p>Now, all objects will be drawn with a line width of 20 pixels, unless they explicitly set their own <code>lineWidth</code> property.</p>
<p>Note that you can not set default animations on <code>glc.styles</code>, only single values. Animations are only parsed within an object's property definition.</p>
<p>There is one additional property on <code>glc.styles</code> that does not apply to individual objects:</p>
<pre><code>backgroundColor "#ffffff"</code></pre>
<p>This is a global style only. The <code>backgroundColor</code> is only used to draw the main background of the animation and is not used by objects. It is not animatable. If you want to animate the background color, just create a rect behind all the other objects and animate its color.</p>
<p>You <em>can</em> create a background color by doing something like <code>"rgba(0, 0, 0, 0.1)"</code>. This has the result of partially covering the previous frame with black. After multiple frames, the background becomes mostly black, but you will sometimes see "trails" of moving objects. This can be used to create some great effects. Note, that the non-standard 8-digit hex string like <code>"#80000000"</code> cannot be used here because this value is not parsed by the GLC color parsing system.</p>
<p>All of the other styles do pretty much what they do in the HTML5 Canvas drawing API. The exceptions are:</p>
<ul>
<li>The <code>lineDash</code> property, which is a <code>setLineDash</code> method on the 2d rendering context.</li>
<li><code>translationX</code> and <code>translationY</code>, which are <code>translate(x, y)</code> on the context.</li>
<li>The fact that you can specify hex colors with an alpha channel here, which you can't do directly on the 2d renering context. e.g. <code>"#80ff0000"</code></li>
<li>The <code>shake</code> property adds an adjustable jitter to any object it is applied to, randomly changing its position by a small amount on each frame.</li>
<li>The <code>blendMode</code> property maps to the Canvas <code>globalCompositeOperation</code> property. It's exactly the same. I just didn't feel the need to make you type "globalCompositeOperation" every time you need to use it.</li>
</ul>
<h3 id="the-color-module"><a name="color"></a>The Color Module</h3>
<p>Although colors are already pretty powerful in GLC, I added a color module that makes defining colors even easier. You can access this as <code>glc.color</code> and it is now aliased in the template to simply be <code>color</code>.</p>
<p>The color module contains several methods that return color strings that can be used for fill styles, stroke styles, shadow colors or background colors. Here are all the methods:</p>
<pre><code>- color.rgb(r, g, b)
- color.rgba(r, g, b, a)
- color.gray(shade)
- color.randomRGB()
- color.randomRGB(min, max)
- color.randomGray()
- color.randomGray(min, max)
- color.num(number)
- color.hsv(h, s, v)
- color.hsva(h, s, v, a)
- color.animHSV(h, s, v)
- color.animHSVA(h, s, v, a)
- color.randomHSV(minH, maxH, minS, maxS, minV, maxV)</code></pre>
<p>Some of these simply make it easier to define colors using numbers for the component channels.</p>
<pre><code>fillStyle: color.rgb(255, 128, 0)</code></pre>
<p>or...</p>
<pre><code>fillStyle: color.rgba(255, 128, 0, 0.5)</code></pre>
<p>The <code>color.num</code> method lets you pass in a single integer that will be converted to a color string. This is usually done with a hexadecimal based number.</p>
<pre><code>fillStyle: color.num(0xff8000)</code></pre>
<p>Note that the <code>num</code> method only supports 24-bit numbers, so no alpha channel. Which is to say that the color will always be fully opaque.</p>
<p>You can also easily define a grayscale color with the <code>color.gray</code> method. Just pass it a value from 0 to 255.</p>
<pre><code>fillStyle: color.gray(128)</code></pre>
<p>Then there are methods for generating random colors or grays. <code>color.randomRGB()</code> will return a random color with full alpha.</p>
<pre><code>fillStyle: color.randomRGB()</code></pre>
<p>You can also pass minimum and maximum parameters to this method. For example, the following will generate a random color where all of the components have a value between 0 and 128. This will be a darker color, whatever it winds up being.</p>
<pre><code>fillStyle: color.randomRGB(0, 128)</code></pre>
<p>Whereas this example will generate colors where all the components have higher values - between 128 and 255:</p>
<pre><code>fillStyle: color.randomRGB(128, 255)</code></pre>
<p>Then there's the <code>color.randomGray()</code> method. This works the same way. With no parameters, you'll get a random gray from the full spectrum of grays.</p>
<pre><code>fillStyle: color.randomGray()</code></pre>
<p>Or you can pass in min and max values to set a range of grays where your random one will be pulled from.</p>
<pre><code>fillStyle: color.randomGray(0, 128)</code></pre>
<p>Finally, there are two methods for getting HSV (hue, saturation, value) based colors. First there is <code>color.hsv(h, s, v)</code>. Here, you pass a value from 0 to 360 for hue. This determines the base color. Then a value from 0 to 1 for saturation and 0 to 1 for value. Saturation controls how much of the base hue will be in the final color, where 1 is a fully saturated value and 0 will give you white. And value controls how dark the color is, where 1 is as bright as it can be and 0 will be black.</p>
<pre><code>fillStyle: color.hsv(30, 1, 1)</code></pre>
<p>There's also a version with alpha:</p>
<pre><code>fillStyle: color.hsva(30, 1, 1, 0.5)</code></pre>
<p>And, of course, there's a random method for hsv: <code>color.randomHue(min, max, s, v)</code>. With this one, the <code>min</code> and <code>max</code> parameters, as well as <code>s</code> and <code>v</code>, are all required. The following will generate colors with a orange to yellow hue:</p>
<pre><code>fillStyle: color.randomHSV(30, 60, 1, 1, 1, 1)</code></pre>
<p>A note on animating with the color module. All of the methods discussed so far return valid color strings, so they can all be animated just as if you'd assigned any other basic string. For example:</p>
<pre><code>fillStyle: [color.rgb(255, 0, 0), color.gray(128)]</code></pre>
<p>And you can mix and match them with any other color strings:</p>
<pre><code>fillStyle: [color.randomRGB(), "blue"]</code></pre>
<p>The only caveat is on using the <code>color.hsv</code> method. The values you assign here will be converted to a color string - which is rgb based - BEFORE animating. So you may not get the result you're looking for when animating between two hsv-defined colors. Take the following example:</p>
<pre><code>fillStyle: [color.hsv(0, 1, 1), color.hsv(360, 1, 1)]</code></pre>
<p>You might expect that to animate through the full spectrum of colors, giving you a rainbow effect. That's not what you'll get though. Actually <code>hsv(0, 1, 1)</code> will be converted to <code>"#ff0000"</code>. And <code>hsv(1, 1, 1)</code> will convert to the exact same thing. So you'll be animating from red to red - no visible animation at all.</p>
<p>That's what the <code>color.animHSV</code> method is for. This takes six values - start and end parameters for each of h, s and v, and will animate between them as you would expect.</p>
<pre><code>fillStyle: color.animHSV(startH, endH, startS, endS, startV, endV)</code></pre>
<p>This will recalculate a new hsv value on each frame, so it is actually animating through various hues. If you try this one, I suggest you try it in single mode, no easing, with a duration of at least 5. Otherwise, the entire spectrum will be moving by so quickly, it will just look like random flashing colors. Animating hues often works better for smaller ranges, such as a min and max of 30 and 60, which will move between an orange and a yellow color.</p>
<p>And, for the sake of completeness, the alpha version:</p>
<pre><code>fillStyle: color.animHSVA(startH, endH, startS, endS, startV, endV, startA, endA)</code></pre>
<p>For a bit more in depth discussion of the color module, <a href="http://www.gifloopcoder.com/blog/2015/11/22/new-the-color-module/">check out this post</a>.</p>
<h3 id="gradients"><a name="gradients"></a>Gradients</h3>
<p>Gradients, even in native canvas, are complex. Creating a single gradient is complicated enough, so it was a challenge to allow for a method of animating gradients without making it any more complicated. One of the main design decisions, good or bad, in GLC was to match the native canvas drawing API as much as possible. In canvas, you create gradients by calling <code>context.createLinearGradient()</code> or <code>context.createRadialGradient()</code> and then adding colors at specific spots in the gradient by calling <code>gradient.addColorStop()</code>. This is well documented elsewhere on the web, so I'm not going to go into that much here.</p>
<p>But basically, you'll be using the same API to make gradients in GLC. The big difference is that the gradients you create in GLC will be animatable, just like everything else.</p>
<p>The methods for creating gradients have been added to the color module (which again is at <code>glc.color</code>, and aliased to simply <code>color</code> in the <code>template.js</code> file).</p>
<p>So, to create a linear gradient, you would say:</p>
<pre><code>var gradient = color.createLinearGradient(x0, y0, x1, y1);</code></pre>
<p>This creates a linear gradient that starts at point <code>x0, y0</code> and goes to <code>x1, y1</code>. Now we need to add color stops. Say we want the color to be red at point <code>x0, y0</code> and gradiently change to blue when it reaches <code>x1, y1</code>. We say:</p>
<pre><code>
var gradient = color.createLinearGradient(x0, y0, x1, y1);
<b>gradient.addColorStop(0, "red");
gradient.addColorStop(1, "blue");</b>
</code></pre>
<p>You can add as many stops as you want. Just ensure that you keep the positions in the range of 0 - 1.</p>
<pre><code>
var gradient = color.createLinearGradient(x0, y0, x1, y1);
gradient.addColorStop(0, "red");
<b>gradient.addColorStop(0.5, "green");</b>
gradient.addColorStop(1, "blue");
</code></pre>
<p>Now, the gradient will start at red, move through green at the midway point, and end at blue. We can then simply assign this to the <code>fillStyle</code> or <code>strokeStyle</code> property of any object we are drawing. So here's what we have:</p>
<pre><code>
function onGLC(glc) {
glc.loop();
glc.size(200, 200);
var list = glc.renderList,
width = glc.w,
height = glc.h,
color = glc.color;
var gradient = color.createLinearGradient(0, -100, 0, 100);
gradient.addColorStop(0, "red");
gradient.addColorStop(0.5, "green");
gradient.addColorStop(1, "blue");
list.addCircle({
x: 100,
y: 100,
radius: 100,
<b>fillStyle: gradient</b>
});
}
</code></pre>
<p>Here, the gradient goes from point 0, -100 to point 0, 100. This is relative to the center of the circle we are drawing. Since the circle has a radius of 100, the gradient will go from the top of the circle to the bottom. And here's what this gives us:</p>
<figure>
<img src="images/3.2.png" alt="" />
</figure>
<p>The center of the object distinction is unique to GLC. In native canvas, the gradient is always centered at the top left of the canvas itself. This makes calculating your gradient sizes and positions to match what you're drawing a bit tricky.</p>
<p>But for many of the objects in GLC the gradient is centered on the object, so things generally become a whole lot simpler.</p>
<p>The gradient is centered on the object for the following shapes: circle, gear, heart, oval, poly, rect, star and text. These objects all have a size and position that is known beforehand. Also, these are the objects most likely to be filled, and gradients are generally more commonly used on fills than on strokes. So this makes filling these objects with a gradient very easy.</p>
<p>When using gradients on other objects, such as curves, lines and paths, the gradient will most likely start at the default top left of the canvas and you'll have to work out where you want it from there, just like in native canvas. In some objects, such as the ray, the gradient will be centered on the first point of the ray. So you might need to experiment a bit to see where the gradient is actually starting and adjust it from there.</p>
<p>For radial gradients, the process is the same, but you'd call <code>color.createRadialGradient(x0, y0, r0, x1, y1, r1)</code>. Again, you're specifying two points, but also two radii. Basically the gradient goes between two circles. Usually these are somewhat concentric circles, with one very small, and the other the size of your object. So we have this:</p>
<pre><code>
function onGLC(glc) {
glc.loop();
glc.size(200, 200);
var list = glc.renderList,
width = glc.w,
height = glc.h,
color = glc.color;
<b>var gradient = color.createRadialGradient(0, 0, 0, 0, 0, 100);
gradient.addColorStop(0, "red");
gradient.addColorStop(0.5, "green");
gradient.addColorStop(1, "blue");</b>
list.addCircle({
x: 100,
y: 100,
radius: 100,
<b>fillStyle: gradient</b>
});
}
</code></pre>
<p>The only difference here is that we created a radial gradient. The first point is 0, 0 with a radius of 0. The second point is also 0, 0, with a radius of 100. Color stops are the same. So we get:</p>
<figure>
<img src="images/3.3.png" alt="" />
</figure>
<p>Now, on to animating! A lot of work went into making this as easy as possible while still following the canvas API. In the end, all we have to do is create two gradients, and assign them as an array to <code>fillStyle</code>. GCL will animate between them. It will animate the points and radii of the gradients, and the positions and colors of all the color stops!</p>
<p>Here you go:</p>
<pre><code>
function onGLC(glc) {
glc.loop();
glc.size(200, 200);
var list = glc.renderList,
width = glc.w,
height = glc.h,
color = glc.color;
<b>var gradient1 = color.createRadialGradient(0, 0, 0, 0, 0, 100);
gradient1.addColorStop(0, "red");
gradient1.addColorStop(0.5, "green");
gradient1.addColorStop(1, "blue");
var gradient2 = color.createRadialGradient(0, 0, 0, 0, 0, 100);
gradient2.addColorStop(0, "green");
gradient2.addColorStop(0.5, "blue");
gradient2.addColorStop(1, "red");</b>
list.addCircle({
x: 100,
y: 100,
radius: 100,
<b>fillStyle: [gradient1, gradient2]</b>
});
}
</code></pre>
<p>Here, all I did was change the colors, but you could change just about anything. The result:</p>
<figure>
<img src="images/3.4.gif" alt="" />
</figure>
<p>Now before you ask, NO, it's not possible to animate between different kinds of gradients. For example, you can't animate between a solid fill and a gradient, or between a linear and radial gradient. You could, of course, create two gradients, and have the colors all the same in one. This would, in effect, create a solid fill or stroke. But since it's really a gradient, it would animate to a different gradient.</p>
<p>Also, the number of color stops in the two gradients should be equal. If they aren't, some color stops might be dropped, or your computer might blow up.</p>
</body>
</html>