inkcite 1.14.0 → 1.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/assets/init/config.yml +2 -0
- data/assets/social/facebook.png +0 -0
- data/assets/social/instagram.png +0 -0
- data/assets/social/pintrest.png +0 -0
- data/assets/social/twitter.png +0 -0
- data/inkcite.gemspec +2 -2
- data/lib/inkcite.rb +1 -0
- data/lib/inkcite/cli/base.rb +32 -6
- data/lib/inkcite/cli/preview.rb +24 -28
- data/lib/inkcite/cli/server.rb +22 -19
- data/lib/inkcite/cli/test.rb +1 -1
- data/lib/inkcite/email.rb +8 -4
- data/lib/inkcite/facade/animation.rb +4 -0
- data/lib/inkcite/facade/keyframe.rb +26 -3
- data/lib/inkcite/image/base.rb +38 -0
- data/lib/inkcite/image/guetzli_minifier.rb +62 -0
- data/lib/inkcite/image/image_minifier.rb +143 -0
- data/lib/inkcite/image/image_optim_minifier.rb +90 -0
- data/lib/inkcite/image/mozjpeg_minifier.rb +92 -0
- data/lib/inkcite/mailer.rb +201 -112
- data/lib/inkcite/minifier.rb +2 -146
- data/lib/inkcite/post_processor.rb +13 -0
- data/lib/inkcite/renderer.rb +19 -0
- data/lib/inkcite/renderer/background.rb +53 -14
- data/lib/inkcite/renderer/base.rb +29 -15
- data/lib/inkcite/renderer/button.rb +1 -1
- data/lib/inkcite/renderer/carousel.rb +245 -0
- data/lib/inkcite/renderer/container_base.rb +10 -0
- data/lib/inkcite/renderer/div.rb +1 -3
- data/lib/inkcite/renderer/fireworks.rb +54 -40
- data/lib/inkcite/renderer/footnote.rb +22 -2
- data/lib/inkcite/renderer/image.rb +11 -0
- data/lib/inkcite/renderer/image_base.rb +3 -6
- data/lib/inkcite/renderer/in_browser.rb +4 -0
- data/lib/inkcite/renderer/link.rb +39 -12
- data/lib/inkcite/renderer/mobile_image.rb +1 -1
- data/lib/inkcite/renderer/responsive.rb +9 -1
- data/lib/inkcite/renderer/social.rb +31 -3
- data/lib/inkcite/renderer/special_effect.rb +22 -13
- data/lib/inkcite/renderer/sup.rb +32 -0
- data/lib/inkcite/renderer/table_base.rb +3 -0
- data/lib/inkcite/renderer/topic.rb +76 -0
- data/lib/inkcite/renderer/trademark.rb +47 -0
- data/lib/inkcite/renderer/video_preview.rb +3 -2
- data/lib/inkcite/uploader.rb +2 -3
- data/lib/inkcite/util.rb +51 -0
- data/lib/inkcite/version.rb +1 -1
- data/lib/inkcite/view.rb +140 -54
- data/lib/inkcite/view/context.rb +1 -31
- data/lib/inkcite/view/media_query.rb +6 -0
- data/test/animation_spec.rb +7 -0
- data/test/parser_spec.rb +1 -1
- data/test/renderer/background_spec.rb +16 -12
- data/test/renderer/div_spec.rb +11 -0
- data/test/renderer/footnote_spec.rb +5 -1
- data/test/renderer/image_spec.rb +51 -28
- data/test/renderer/link_spec.rb +20 -8
- data/test/renderer/lorem_spec.rb +2 -2
- data/test/renderer/mobile_image_spec.rb +6 -0
- data/test/renderer/mobile_style_spec.rb +3 -3
- data/test/renderer/redacted_spec.rb +2 -2
- data/test/renderer/social_spec.rb +6 -6
- data/test/renderer/table_spec.rb +4 -0
- data/test/renderer/topic_spec.rb +28 -0
- data/test/renderer/trademark_spec.rb +40 -0
- data/test/renderer/video_preview_spec.rb +1 -1
- data/test/test_helper.rb +14 -0
- data/test/view_spec.rb +4 -0
- metadata +26 -12
- data/assets/init/image_optim.yml +0 -37
@@ -148,7 +148,7 @@ module Inkcite
|
|
148
148
|
html << %Q( mobile="fill"}\n)
|
149
149
|
html << "{td align=center"
|
150
150
|
html << " height=#{cfg.height} valign=middle" if cfg.height > 0
|
151
|
-
html <<
|
151
|
+
html << %Q( font="#{cfg.font}" color="none")
|
152
152
|
html << " line-height=#{cfg.line_height}" unless cfg.line_height.blank?
|
153
153
|
html << %Q( font-size="#{cfg.font_size}") if cfg.font_size > 0
|
154
154
|
html << %Q( font-weight="#{cfg.font_weight}") unless cfg.font_weight.blank?
|
@@ -0,0 +1,245 @@
|
|
1
|
+
module Inkcite
|
2
|
+
module Renderer
|
3
|
+
|
4
|
+
# Interactive image carousel based on FreshInbox's technique
|
5
|
+
# http://freshinbox.com/resources/tools/carousel/
|
6
|
+
#
|
7
|
+
# Usage:
|
8
|
+
#
|
9
|
+
# {carousel width=450 height=280 href="..."}
|
10
|
+
# {carousel-img id="..." src="..."}
|
11
|
+
# {carousel-img id="..." src="..." href="..."}
|
12
|
+
# {carousel-img id="..." src="..."}
|
13
|
+
# {/carousel}
|
14
|
+
#
|
15
|
+
class Carousel < ContainerBase
|
16
|
+
|
17
|
+
class Image < ImageBase
|
18
|
+
|
19
|
+
def render tag, opt, ctx
|
20
|
+
|
21
|
+
# Get a reference to the tag stack that contains these frames
|
22
|
+
tag_stack = ctx.tag_stack :carousel
|
23
|
+
|
24
|
+
# Get the opts held by the parent carousel.
|
25
|
+
carousel_opt = tag_stack.opts
|
26
|
+
|
27
|
+
#opt[:_src] = image_url(opt[:src], opt, ctx, false, false)
|
28
|
+
|
29
|
+
# Initialize the frames array
|
30
|
+
carousel_opt[:frames] << opt
|
31
|
+
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def render tag, opt, ctx
|
38
|
+
|
39
|
+
html = []
|
40
|
+
|
41
|
+
# Get a reference to the tag stack
|
42
|
+
tag_stack = ctx.tag_stack :carousel
|
43
|
+
|
44
|
+
# All the rendering heavylifting is done when the closing tag is encountered
|
45
|
+
# meaning we've indexed each of the carousel-img entries between it and the
|
46
|
+
# opening tag.
|
47
|
+
if tag == '/carousel'
|
48
|
+
|
49
|
+
# Remove the previously opened opts from the tag stack.
|
50
|
+
open_opt = tag_stack.pop
|
51
|
+
|
52
|
+
# Unique ID of this carousel to uniquely identify the classes
|
53
|
+
uuid = open_opt[:uuid]
|
54
|
+
|
55
|
+
# Link ID prefix
|
56
|
+
carousel_id = open_opt[:id]
|
57
|
+
|
58
|
+
# Width of the primary image
|
59
|
+
width = open_opt[:width].to_i
|
60
|
+
height = open_opt[:height].to_i
|
61
|
+
ctx.error('Missing carousel dimensions', { :id => carousel_id, :width => width, :height => height }) if height <= 0 || width <= 0
|
62
|
+
|
63
|
+
# Grab the array of frames and count the total number of frames.
|
64
|
+
frames = open_opt[:frames]
|
65
|
+
total_frames = frames.count
|
66
|
+
|
67
|
+
# This {table} Helper wraps the entire carousel.
|
68
|
+
table = Element.new('table', :width => width, :class => 'crsl', :mobile => :fill)
|
69
|
+
|
70
|
+
# Determine if a background color for the entire carousel has been specified
|
71
|
+
# using either bgcolor or background-color. If so, pass it on to the table Helper.
|
72
|
+
bgcolor = detect_bgcolor(open_opt)
|
73
|
+
table[:bgcolor] = quote(bgcolor) unless none?(bgcolor)
|
74
|
+
|
75
|
+
html << table.to_helper
|
76
|
+
|
77
|
+
td = Element.new('td')
|
78
|
+
mix_font td, open_opt, ctx
|
79
|
+
html << td.to_s
|
80
|
+
|
81
|
+
html << %q({not-outlook})
|
82
|
+
html << %q(<input type=radio class="crsl-radio" style="display:none !important;" checked>)
|
83
|
+
html << %q({/not-outlook})
|
84
|
+
|
85
|
+
# Div to hold the carousel contents.
|
86
|
+
html << %q({div})
|
87
|
+
|
88
|
+
html << %q({not-outlook})
|
89
|
+
html << Element.new('div', :class => %Q("crsl-wrap crsl-#{uuid}"), :style => {
|
90
|
+
:width => pct(100), :height => px(0), MAX_HEIGHT => px(0), :overflow => :hidden, TEXT_ALIGN => :center
|
91
|
+
}).to_s
|
92
|
+
|
93
|
+
# Array of hidden radio buttons that manage which thumbnail is selected.
|
94
|
+
total_frames.times do |n|
|
95
|
+
box_index = total_frames - n
|
96
|
+
|
97
|
+
html << %q(<label>)
|
98
|
+
html << %Q(<input type="radio" name="crsl#{uuid}" class="crsl-radio-#{box_index}" style="display:none !important;"#{' checked' if box_index == 1}>)
|
99
|
+
html << %q(<span>)
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
total_frames.times do |n|
|
104
|
+
box_index = n + 1
|
105
|
+
frame_opt = frames[n]
|
106
|
+
frame_id = frame_opt[:id] || "#{carousel_id}-#{box_index}"
|
107
|
+
|
108
|
+
href = frame_opt[:href] || open_opt[:href]
|
109
|
+
src = frame_opt[:src]
|
110
|
+
|
111
|
+
caption = frame_opt[:caption]
|
112
|
+
alt = frame_opt[:alt] || caption
|
113
|
+
|
114
|
+
html << %Q(<div class="crsl-content-#{box_index} crsl-content">)
|
115
|
+
html << %Q({a id="#{frame_id}" href="#{href}"}) unless href.blank?
|
116
|
+
html << %Q({img src="#{src}" width=#{width} height=#{height} alt="#{alt}" max-height=0 mobile="fill"})
|
117
|
+
html << %q({/a}) unless href.blank?
|
118
|
+
|
119
|
+
unless caption.blank?
|
120
|
+
caption_div = Element.new('div', :class => 'crsl-caption', :style => { :padding => '5px 0 10px' })
|
121
|
+
mix_font caption_div, open_opt, ctx
|
122
|
+
html << caption_div.to_s
|
123
|
+
html << caption
|
124
|
+
html << '</div>'
|
125
|
+
end
|
126
|
+
|
127
|
+
html << %q(</div>)
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
thumbnail_width = 50
|
132
|
+
thumnail_height = (thumbnail_width / width) * height.round(0)
|
133
|
+
|
134
|
+
total_frames.times do |n|
|
135
|
+
box_index = total_frames - n
|
136
|
+
frame_opt = frames[n]
|
137
|
+
|
138
|
+
src = frame_opt[:src]
|
139
|
+
|
140
|
+
html << %q(<span class="crsl-thumb" style="display:none;">)
|
141
|
+
html << %Q({img src="#{src}" width=#{width} height=#{height} max-height=0})
|
142
|
+
html << %q(</span>)
|
143
|
+
html << %q(</span>)
|
144
|
+
html << %q(</label>)
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
html << %q(</div>)
|
149
|
+
html << %q({/not-outlook})
|
150
|
+
|
151
|
+
# Fallback
|
152
|
+
fallback_frame_opt = frames[0]
|
153
|
+
fallback_href = fallback_frame_opt[:href] || open_opt[:href]
|
154
|
+
fallback_src = open_opt[OUTLOOK_SRC] || fallback_frame_opt[:src]
|
155
|
+
|
156
|
+
html << %q(<div class="fallback"><div class="crsl-content">)
|
157
|
+
html << %Q({a id="#{carousel_id}" href="#{fallback_href}"}{img src="#{fallback_src}" width=#{width} height=#{height} alt="#{fallback_frame_opt[:caption]}" mobile="fill"}{/a})
|
158
|
+
html << %q(</div></div>)
|
159
|
+
|
160
|
+
html << '{/div}'
|
161
|
+
html << '</td>'
|
162
|
+
html << '{/table}'
|
163
|
+
|
164
|
+
styles = []
|
165
|
+
|
166
|
+
if uuid == 1
|
167
|
+
|
168
|
+
styles << Inkcite::Renderer::Style.new('input', ctx, { :display => :none })
|
169
|
+
|
170
|
+
styles << Inkcite::Renderer::Style.new('.crsl-radio:checked + * .crsl-wrap', ctx, { :'height' => 'auto !important', MAX_HEIGHT => 'none !important;', :'line-height' => 0 })
|
171
|
+
|
172
|
+
styles << Inkcite::Renderer::Style.new('.crsl-wrap span', ctx, { :'font-size' => 0, :'line-height' => 0 })
|
173
|
+
|
174
|
+
styles << Inkcite::Renderer::Style.new('.crsl-radio:checked + * .crsl-wrap .crsl-content', ctx, { :'display' => 'none', MAX_HEIGHT => px(0), :'overflow' => 'hidden' })
|
175
|
+
|
176
|
+
styles << Inkcite::Renderer::Style.new('.crsl-wrap .crsl-thumb', ctx, { :cursor => 'pointer', :display => 'inline-block !important', :width => '17.5%', :margin => '1% 0.61%', :border => "2px solid #{DEFAULT_BORDER_COLOR}" })
|
177
|
+
|
178
|
+
# hide for thunderbird as it doesn't support checkboxes
|
179
|
+
styles << Inkcite::Renderer::Style.new('.moz-text-html .crsl-thumb', ctx, { :display => 'none !important' })
|
180
|
+
|
181
|
+
styles << Inkcite::Renderer::Style.new('.crsl-wrap .crsl-thumb:hover', ctx, { :border => "2px solid #{DEFAULT_HOVER_COLOR}" })
|
182
|
+
|
183
|
+
styles << Inkcite::Renderer::Style.new('.crsl-wrap input:checked + span > span', ctx, { BORDER_COLOR => DEFAULT_HOVER_COLOR })
|
184
|
+
|
185
|
+
styles << Inkcite::Renderer::Style.new('.crsl-wrap .crsl-thumb img', ctx, { :width => '100%', :height => 'auto' })
|
186
|
+
|
187
|
+
styles << Inkcite::Renderer::Style.new('.crsl-wrap img', ctx, { MAX_HEIGHT => 'none !important' })
|
188
|
+
|
189
|
+
styles << Inkcite::Renderer::Style.new('.crsl-radio:checked + * .fallback', ctx, { :display => 'none !important', MAX_HEIGHT => px(0), :height => px(0), :overflow => 'hidden' })
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
# Configurable border color
|
194
|
+
border_color = open_opt[BORDER_COLOR]
|
195
|
+
unless border_color.blank?
|
196
|
+
border_color = hex(border_color)
|
197
|
+
styles << Inkcite::Renderer::Style.new(".crsl-#{uuid} .crsl-thumb", ctx, { BORDER_COLOR => border_color }) if DEFAULT_BORDER_COLOR != border_color
|
198
|
+
end
|
199
|
+
|
200
|
+
# Configurable hover color
|
201
|
+
hover_color = open_opt[:'hover-color']
|
202
|
+
unless hover_color.blank?
|
203
|
+
hover_color = hex(hover_color)
|
204
|
+
if DEFAULT_HOVER_COLOR != hover_color
|
205
|
+
styles << Inkcite::Renderer::Style.new(".crsl-#{uuid} input:checked + span > span", ctx, { BORDER_COLOR => hover_color })
|
206
|
+
styles << Inkcite::Renderer::Style.new(".crsl-#{uuid} .crsl-thumb:hover", ctx, { BORDER_COLOR => hover_color })
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
frame_checked_style_name = total_frames.times.collect { |n| ".crsl-wrap .crsl-radio-#{n + 1}:checked + span .crsl-content-#{n + 1}" }.join(",\n")
|
211
|
+
styles << Inkcite::Renderer::Style.new(frame_checked_style_name, ctx, { :display => 'block !important', MAX_HEIGHT => 'none !important', :overflow => 'visible !important' })
|
212
|
+
|
213
|
+
ctx.styles << styles.join("\n") unless styles.blank?
|
214
|
+
|
215
|
+
|
216
|
+
else
|
217
|
+
|
218
|
+
# Get a unique ID for this carousel
|
219
|
+
opt[:uuid] = uuid = ctx.unique_id :carousel
|
220
|
+
|
221
|
+
# Confirm that a unique ID prefix has been provided by the designer or
|
222
|
+
# inherit one from the unique count.
|
223
|
+
opt[:id] = "crsl#{uuid}" if opt[:id].blank?
|
224
|
+
|
225
|
+
# Initialize the array of frames that will be collected and
|
226
|
+
# assembled when this carousel close tag is encountered.
|
227
|
+
opt[:frames] = []
|
228
|
+
|
229
|
+
# Push this carousel onto the stack as the parent of the
|
230
|
+
# frames that will be enclosed.
|
231
|
+
tag_stack << opt
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
html.join("\n")
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
DEFAULT_HOVER_COLOR = '#444444'
|
241
|
+
DEFAULT_BORDER_COLOR = '#bbbbbb'
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -37,6 +37,16 @@ module Inkcite
|
|
37
37
|
element.to_s
|
38
38
|
end
|
39
39
|
|
40
|
+
def mix_height element, opt, ctx
|
41
|
+
|
42
|
+
height = opt[:height].to_i
|
43
|
+
element.style[:height] = px(height) if height > 0
|
44
|
+
|
45
|
+
mobile_height = opt[MOBILE_HEIGHT].to_i
|
46
|
+
element.mobile_style[:height] = px(mobile_height) if mobile_height > 0
|
47
|
+
|
48
|
+
end
|
49
|
+
|
40
50
|
def mix_width element, opt, ctx
|
41
51
|
|
42
52
|
width = opt[:width]
|
data/lib/inkcite/renderer/div.rb
CHANGED
@@ -7,16 +7,12 @@ module Inkcite
|
|
7
7
|
|
8
8
|
private
|
9
9
|
|
10
|
-
# Each firework will move this many positions during the
|
11
|
-
# entire animation.
|
12
|
-
TOTAL_POSITIONS = 5
|
13
|
-
|
14
10
|
# The number of hue degrees to randomly alter the hue of each spark
|
15
11
|
HUE_RANGE = :'hue-range'
|
16
12
|
|
17
13
|
# Names of the attributes controlling min and max explosion size.
|
18
|
-
|
19
|
-
|
14
|
+
DIAMETER_MIN = :'min-diameter'
|
15
|
+
DIAMETER_MAX = :'max-diameter'
|
20
16
|
|
21
17
|
# Attributes used for color generation
|
22
18
|
SATURATION = 100
|
@@ -26,9 +22,13 @@ module Inkcite
|
|
26
22
|
|
27
23
|
count = sfx.count
|
28
24
|
|
25
|
+
# Make sure the total number of stops (formerly TOTAL_POSITIONS)
|
26
|
+
# is specified as an integer.
|
27
|
+
sfx[:stops] = sfx[:stops].to_i
|
28
|
+
|
29
29
|
# Total number of firework instances multiplied by the number
|
30
30
|
# of positions each firework will cycle through.
|
31
|
-
position_count =
|
31
|
+
position_count = sfx[:stops] * count
|
32
32
|
positions = sfx.equal_distribution(sfx.position_range, position_count)
|
33
33
|
|
34
34
|
sfx[:x_positions] = positions
|
@@ -43,22 +43,30 @@ module Inkcite
|
|
43
43
|
def create_explosion_animation n, hue, duration, delay, sfx
|
44
44
|
|
45
45
|
# Calculate the radius size for this explosion
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
radius = rand(radius_range).round(0)
|
50
|
-
half_radius = radius / 2.0
|
46
|
+
min_diameter = sfx[DIAMETER_MIN].to_i
|
47
|
+
max_diameter = sfx[DIAMETER_MAX].to_i
|
48
|
+
diameter_range = (min_diameter..max_diameter)
|
51
49
|
|
52
50
|
hue_range = (sfx[HUE_RANGE] || 40).to_i
|
53
51
|
hue_range_2x = hue_range * 2
|
54
52
|
|
55
53
|
sparks = sfx[:sparks].to_i
|
56
54
|
|
55
|
+
angle = 0
|
56
|
+
angle_step = 360 / sparks.to_f
|
57
|
+
|
57
58
|
box_shadow = sparks.times.collect do |n|
|
58
59
|
|
60
|
+
# Pick a random angle.
|
61
|
+
angle_radians = angle * PI_OVER_180
|
62
|
+
angle += angle_step
|
63
|
+
|
64
|
+
# Pick a random radius
|
65
|
+
radius = rand(diameter_range) / 2.0
|
66
|
+
|
59
67
|
# Pick a random position for this spark to move to
|
60
|
-
x = (
|
61
|
-
y = (
|
68
|
+
x = (radius * Math::cos(angle_radians)).round(0)
|
69
|
+
y = (radius * Math::sin(angle_radians)).round(0)
|
62
70
|
|
63
71
|
# Randomly pick a slightly different hue for this spark
|
64
72
|
_hue = hue + hue_range - rand(hue_range_2x)
|
@@ -70,7 +78,7 @@ module Inkcite
|
|
70
78
|
anim = Inkcite::Animation.new(sfx.animation_class_name(n, 'bang'), sfx.ctx)
|
71
79
|
anim.duration = duration
|
72
80
|
anim.delay = delay if delay > 0
|
73
|
-
anim.timing_function = Inkcite::Animation::
|
81
|
+
anim.timing_function = Inkcite::Animation::EASE_OUT_QUART
|
74
82
|
anim.add_keyframe 100, { BOX_SHADOW => box_shadow.join(', ') }
|
75
83
|
|
76
84
|
sfx.animations << anim
|
@@ -78,11 +86,11 @@ module Inkcite
|
|
78
86
|
anim
|
79
87
|
end
|
80
88
|
|
81
|
-
def
|
89
|
+
def create_decay_animation sfx
|
82
90
|
|
83
|
-
anim = Animation.new(
|
91
|
+
anim = Animation.new(DECAY_ANIMATION_NAME, sfx.ctx)
|
84
92
|
|
85
|
-
# All fireworks fade to zero opacity and size by the end of the
|
93
|
+
# All fireworks fade to zero opacity and size by the end of the decay cycle.
|
86
94
|
keyframe = anim.add_keyframe 100, { :opacity => 0, :width => 0, :height => 0 }
|
87
95
|
|
88
96
|
# Check to see if gravity has been specified for the fireworks. If so
|
@@ -96,9 +104,7 @@ module Inkcite
|
|
96
104
|
|
97
105
|
def create_position_animation n, duration, delay, sfx
|
98
106
|
|
99
|
-
|
100
|
-
# be spent in each position.
|
101
|
-
keyframe_duration = (100 / TOTAL_POSITIONS.to_f)
|
107
|
+
stops = sfx[:stops]
|
102
108
|
|
103
109
|
anim = Inkcite::Animation.new(sfx.animation_class_name(n, 'position'), sfx.ctx)
|
104
110
|
anim.duration = duration
|
@@ -108,17 +114,27 @@ module Inkcite
|
|
108
114
|
x_positions = sfx[:x_positions]
|
109
115
|
y_positions = sfx[:y_positions]
|
110
116
|
|
117
|
+
# This is the percentage amount of the total animation that will
|
118
|
+
# be spent in each position.
|
119
|
+
keyframe_duration = 100.0 / stops.to_f
|
120
|
+
|
111
121
|
percent = 0
|
112
|
-
|
122
|
+
stops.times do |n|
|
113
123
|
|
114
124
|
# Pick a random position for this firework
|
115
125
|
top = y_positions.delete_at(rand(y_positions.length))
|
116
126
|
left = x_positions.delete_at(rand(x_positions.length))
|
117
127
|
|
118
|
-
|
119
|
-
|
128
|
+
# Calculate when the next keyframe will trigger.
|
129
|
+
next_keyframe = percent + keyframe_duration
|
130
|
+
|
131
|
+
# Calculate when this frame should end
|
132
|
+
end_percent = n < stops - 1 ? (next_keyframe - 0.1).round(1) : 100
|
120
133
|
|
121
|
-
percent
|
134
|
+
keyframe = anim.add_keyframe(percent.round(1), { :top => pct(top), :left => pct(left) })
|
135
|
+
keyframe.end_percent = end_percent
|
136
|
+
|
137
|
+
percent = next_keyframe
|
122
138
|
end
|
123
139
|
|
124
140
|
sfx.animations << anim
|
@@ -150,10 +166,10 @@ module Inkcite
|
|
150
166
|
box_shadow = sparks.times.collect { |n| '0 0 #fff' }
|
151
167
|
style[BOX_SHADOW] = box_shadow.join(', ')
|
152
168
|
|
153
|
-
# Create the global
|
169
|
+
# Create the global decay animation that is consistent for all fireworks.
|
154
170
|
# There is no variance in this animation so it is created and added to the
|
155
171
|
# context only once.
|
156
|
-
|
172
|
+
create_decay_animation(sfx)
|
157
173
|
|
158
174
|
end
|
159
175
|
|
@@ -185,15 +201,15 @@ module Inkcite
|
|
185
201
|
# move through each of its positions.
|
186
202
|
position_speed = sfx.rand_speed
|
187
203
|
|
188
|
-
# This is the speed the firework animates it's explosion and
|
204
|
+
# This is the speed the firework animates it's explosion and decay
|
189
205
|
# components - which need to repeat n-number of times based on the
|
190
206
|
# total number of positions.
|
191
|
-
explosion_speed = (position_speed /
|
207
|
+
explosion_speed = (position_speed / sfx[:stops].to_f).round(2)
|
192
208
|
|
193
|
-
gravity_animation = Inkcite::Animation.new(
|
209
|
+
gravity_animation = Inkcite::Animation.new(DECAY_ANIMATION_NAME, sfx.ctx)
|
194
210
|
gravity_animation.duration = explosion_speed
|
195
211
|
gravity_animation.delay = delay if n > 0
|
196
|
-
gravity_animation.timing_function = Inkcite::Animation::
|
212
|
+
gravity_animation.timing_function = Inkcite::Animation::EASE_IN_CUBIC
|
197
213
|
|
198
214
|
composite_animation = Inkcite::Animation::Composite.new
|
199
215
|
composite_animation << create_explosion_animation(n, hue, explosion_speed, delay, sfx)
|
@@ -202,13 +218,6 @@ module Inkcite
|
|
202
218
|
|
203
219
|
style[:animation] = composite_animation
|
204
220
|
|
205
|
-
# # Each firework consists of three separate animations - one to animate
|
206
|
-
# # the explosion, one to fade out/apply gravity and one to move the
|
207
|
-
# # firework through it's fixed positions.
|
208
|
-
# style[:animation] = "1s bang ease-out infinite, 1s gravity ease-in infinite, #{TOTAL_POSITIONS}s position linear infinite"
|
209
|
-
|
210
|
-
# style[ANIMATION_DURATION] = "#{explosion_speed}s, #{explosion_speed}s, #{position_speed}s"
|
211
|
-
|
212
221
|
end
|
213
222
|
|
214
223
|
def defaults opt, ctx
|
@@ -217,15 +226,20 @@ module Inkcite
|
|
217
226
|
:sparks => 50,
|
218
227
|
:count => 2,
|
219
228
|
:gravity => 200,
|
220
|
-
|
221
|
-
|
229
|
+
DIAMETER_MIN => 25,
|
230
|
+
DIAMETER_MAX => 200,
|
222
231
|
SIZE_MIN => 10,
|
223
232
|
SIZE_MAX => 10,
|
224
233
|
SPEED_MIN => 5,
|
225
234
|
SPEED_MAX => 10,
|
235
|
+
:stops => 5,
|
226
236
|
}
|
227
237
|
end
|
228
238
|
|
239
|
+
private
|
240
|
+
|
241
|
+
DECAY_ANIMATION_NAME = 'decay'
|
242
|
+
|
229
243
|
end
|
230
244
|
end
|
231
245
|
end
|