inkcite 1.15.0 → 1.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/inkcite.gemspec +0 -3
- data/lib/inkcite.rb +0 -1
- data/lib/inkcite/cli/base.rb +12 -8
- data/lib/inkcite/cli/preview.rb +0 -10
- data/lib/inkcite/cli/server.rb +9 -1
- data/lib/inkcite/email.rb +5 -10
- data/lib/inkcite/facade/animation.rb +4 -1
- data/lib/inkcite/image_minifier.rb +96 -0
- data/lib/inkcite/mailer.rb +2 -2
- data/lib/inkcite/minifier.rb +1 -1
- data/lib/inkcite/renderer.rb +10 -0
- data/lib/inkcite/renderer/base.rb +47 -2
- data/lib/inkcite/renderer/button.rb +15 -5
- data/lib/inkcite/renderer/container_base.rb +15 -1
- data/lib/inkcite/renderer/image.rb +1 -1
- data/lib/inkcite/renderer/image_base.rb +8 -0
- data/lib/inkcite/renderer/list.rb +104 -0
- data/lib/inkcite/renderer/mobile_image.rb +3 -0
- data/lib/inkcite/renderer/responsive.rb +2 -0
- data/lib/inkcite/renderer/slant.rb +207 -0
- data/lib/inkcite/renderer/snow.rb +15 -1
- data/lib/inkcite/renderer/special_effect.rb +7 -0
- data/lib/inkcite/renderer/sup.rb +18 -4
- data/lib/inkcite/renderer/table.rb +9 -1
- data/lib/inkcite/renderer/table_base.rb +20 -2
- data/lib/inkcite/renderer/td.rb +7 -2
- data/lib/inkcite/renderer/video_preview.rb +1 -1
- data/lib/inkcite/uploader.rb +40 -27
- data/lib/inkcite/version.rb +1 -1
- data/lib/inkcite/view.rb +133 -13
- data/lib/inkcite/view/context.rb +6 -1
- data/lib/inkcite/view/developer_scripts.rb +56 -0
- data/test/renderer/background_spec.rb +4 -4
- data/test/renderer/button_spec.rb +14 -8
- data/test/renderer/div_spec.rb +26 -3
- data/test/renderer/image_spec.rb +9 -4
- data/test/renderer/list_spec.rb +36 -0
- data/test/renderer/mobile_image_spec.rb +5 -0
- data/test/renderer/slant_spec.rb +47 -0
- data/test/renderer/span_spec.rb +12 -1
- data/test/renderer/table_spec.rb +1 -1
- data/test/renderer/td_spec.rb +24 -8
- data/test/renderer/trademark_spec.rb +6 -6
- metadata +11 -50
- data/lib/inkcite/image/base.rb +0 -38
- data/lib/inkcite/image/guetzli_minifier.rb +0 -62
- data/lib/inkcite/image/image_minifier.rb +0 -143
- data/lib/inkcite/image/image_optim_minifier.rb +0 -90
- data/lib/inkcite/image/mozjpeg_minifier.rb +0 -92
@@ -47,6 +47,10 @@ module Inkcite
|
|
47
47
|
@opt[:font] || @ctx[BUTTON_FONT]
|
48
48
|
end
|
49
49
|
|
50
|
+
def font_family
|
51
|
+
@opt[Base::FONT_FAMILY] || @ctx[BUTTON_FONT_FAMILY]
|
52
|
+
end
|
53
|
+
|
50
54
|
def font_size
|
51
55
|
(@opt[Base::FONT_SIZE] || @ctx[BUTTON_FONT_SIZE]).to_i
|
52
56
|
end
|
@@ -96,6 +100,7 @@ module Inkcite
|
|
96
100
|
BUTTON_COLOR = :'button-color'
|
97
101
|
BUTTON_FLOAT = :'button-float'
|
98
102
|
BUTTON_FONT = :'button-font'
|
103
|
+
BUTTON_FONT_FAMILY = :'button-font-family'
|
99
104
|
BUTTON_FONT_SIZE = :'button-font-size'
|
100
105
|
BUTTON_FONT_WEIGHT = :'button-font-weight'
|
101
106
|
BUTTON_HEIGHT = :'button-height'
|
@@ -124,9 +129,13 @@ module Inkcite
|
|
124
129
|
|
125
130
|
cfg = Config.new(ctx, opt)
|
126
131
|
|
132
|
+
no_tag = opt[:'no-tag'] == true
|
133
|
+
|
127
134
|
# Wrap the table in a link to make the whole thing clickable. This works
|
128
135
|
# in most email clients but doesn't work in Outlook (for a change).
|
129
|
-
html <<
|
136
|
+
html << %Q({a id="#{id}" href="#{href}" color="none")
|
137
|
+
html << ' no-tag' if no_tag
|
138
|
+
html << '}'
|
130
139
|
|
131
140
|
# Responsive button is just a highly styled table/td combination with optional
|
132
141
|
# curved corners and a lower bevel (border).
|
@@ -146,10 +155,11 @@ module Inkcite
|
|
146
155
|
html << " width=#{cfg.width}" if cfg.width > 0
|
147
156
|
html << " float=#{cfg.float}" if cfg.float
|
148
157
|
html << %Q( mobile="fill"}\n)
|
149
|
-
html <<
|
150
|
-
html <<
|
158
|
+
html << '{td align=center'
|
159
|
+
html << %Q( height=#{cfg.height} valign=middle) if cfg.height > 0
|
151
160
|
html << %Q( font="#{cfg.font}" color="none")
|
152
|
-
html <<
|
161
|
+
html << %Q( font-family="#{cfg.font_family}") unless cfg.font_family.blank?
|
162
|
+
html << %Q( line-height=#{cfg.line_height}) unless cfg.line_height.blank?
|
153
163
|
html << %Q( font-size="#{cfg.font_size}") if cfg.font_size > 0
|
154
164
|
html << %Q( font-weight="#{cfg.font_weight}") unless cfg.font_weight.blank?
|
155
165
|
|
@@ -164,9 +174,9 @@ module Inkcite
|
|
164
174
|
# clickable.
|
165
175
|
html << %Q({a id="#{id}" href="#{href}" color="#{cfg.color}")
|
166
176
|
html << %Q( letter-spacing="#{cfg.letter_spacing}") unless cfg.letter_spacing.blank?
|
177
|
+
html << %q( no-tag) if no_tag
|
167
178
|
html << %q(})
|
168
179
|
|
169
|
-
|
170
180
|
else
|
171
181
|
|
172
182
|
html << "{/a}{/td}\n{/table}{/a}"
|
@@ -16,7 +16,17 @@ module Inkcite
|
|
16
16
|
|
17
17
|
# Supports both integers and mixed padding (e.g. 10px 20px)
|
18
18
|
padding = opt[:padding]
|
19
|
-
|
19
|
+
unless none?(padding)
|
20
|
+
paddingpx = px(padding)
|
21
|
+
element.style[:padding] = paddingpx
|
22
|
+
|
23
|
+
# Copy the padding into the MSO custom padding attribute for high-DPI
|
24
|
+
# compatibility
|
25
|
+
paddingpx = "#{paddingpx} #{paddingpx} #{paddingpx} #{paddingpx}" unless paddingpx.include?(' ')
|
26
|
+
element.style[MSO_PADDING_ALT] = paddingpx
|
27
|
+
|
28
|
+
end
|
29
|
+
|
20
30
|
|
21
31
|
# Vertical alignment - top, middle, bottom.
|
22
32
|
valign = opt[:valign]
|
@@ -32,6 +42,10 @@ module Inkcite
|
|
32
42
|
# Support for mobile-padding and mobile-padding-(direction)
|
33
43
|
mix_mobile_padding element, opt, ctx
|
34
44
|
|
45
|
+
# White space wrapping can be controlled with mobile-no-wrap or mobile-wrap
|
46
|
+
mobile_white_space = (:nowrap if opt[MOBILE_NOWRAP]) || (:normal if opt[MOBILE_WRAP])
|
47
|
+
element.mobile_style[WHITE_SPACE] = mobile_white_space unless mobile_white_space.nil?
|
48
|
+
|
35
49
|
mix_responsive element, opt, ctx
|
36
50
|
|
37
51
|
element.to_s
|
@@ -25,7 +25,7 @@ module Inkcite
|
|
25
25
|
|
26
26
|
# Need to add an extra space for the email clients (ahem, Gmail,
|
27
27
|
# cough) that don't support alt text with line breaks.
|
28
|
-
alt.gsub!("\n", " \n")
|
28
|
+
alt.gsub!("\n", " \n ")
|
29
29
|
|
30
30
|
# Remove all HTML from the alt text. Ran into a situation where a
|
31
31
|
# custom Helper was applying styled text as image alt text. Since
|
@@ -98,6 +98,14 @@ module Inkcite
|
|
98
98
|
def mix_dimensions img, opt, ctx
|
99
99
|
super
|
100
100
|
DIMENSIONS.each { |dim| img[dim] = opt[dim] }
|
101
|
+
|
102
|
+
# Allow images to be easily scaled by percentage.
|
103
|
+
scale = opt[:scale].to_f
|
104
|
+
scale = 1 if scale > 1
|
105
|
+
scale = 0 if scale < 0
|
106
|
+
|
107
|
+
DIMENSIONS.each { |dim| img[dim] = (opt[dim].to_f * scale).round } if scale > 0 && scale < 1
|
108
|
+
|
101
109
|
end
|
102
110
|
|
103
111
|
private
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Inkcite
|
2
|
+
module Renderer
|
3
|
+
class List < Base
|
4
|
+
|
5
|
+
def render tag, opt, ctx
|
6
|
+
|
7
|
+
html = ''
|
8
|
+
|
9
|
+
# Get the stack of list helpers which will used based on whether this is
|
10
|
+
# a list item or a list open/close
|
11
|
+
tag_stack = ctx.tag_stack(:list)
|
12
|
+
|
13
|
+
# Check for the close of any list
|
14
|
+
if tag == 'ul' || tag == 'ol'
|
15
|
+
html << '{table}'
|
16
|
+
|
17
|
+
# Add the type of list as an attribute so the list item can determine the
|
18
|
+
# appropriate bullet/number.
|
19
|
+
opt[:type] = tag
|
20
|
+
|
21
|
+
# Initialize the child count to zero which will be incremented with each
|
22
|
+
# list child added.
|
23
|
+
opt[:children] = 0
|
24
|
+
|
25
|
+
# Push the options onto the stack so they can be referenced by the list items.
|
26
|
+
tag_stack << opt
|
27
|
+
|
28
|
+
elsif tag == '/ul' || tag == '/ol'
|
29
|
+
html << '</td>{/table}'
|
30
|
+
|
31
|
+
# Remove the previously open list declaration.
|
32
|
+
tag_stack.pop
|
33
|
+
|
34
|
+
elsif tag == 'li'
|
35
|
+
|
36
|
+
# Get the options declared when the list was started.
|
37
|
+
parent_opts = tag_stack.opts
|
38
|
+
|
39
|
+
# Increment the child count in the parent opts
|
40
|
+
child_count = parent_opts[:children] = parent_opts[:children] + 1
|
41
|
+
|
42
|
+
if child_count > 1
|
43
|
+
|
44
|
+
# Add a separator between this and previous list items
|
45
|
+
spacing = (parent_opts[:spacing] || 3).to_i
|
46
|
+
if spacing > 0
|
47
|
+
html << "{div height=#{spacing} line-height=#{spacing} font-size=#{spacing}} {/div}</td>"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Start a new row after the previous one.
|
51
|
+
html << '</tr><tr>'
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
bullet = if parent_opts[:type] == 'ol'
|
56
|
+
"#{child_count}."
|
57
|
+
else
|
58
|
+
detect(opt[:bullet], parent_opts[:bullet], '•')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Lists can be repurposed sans bullets to be tables of items with
|
62
|
+
# adjustable space between them by setting the bullet to none.
|
63
|
+
unless none?(bullet)
|
64
|
+
|
65
|
+
# Start a new <td> to hold the bullet and spacing.
|
66
|
+
bullet_td = Element.new('td', :valign => :top)
|
67
|
+
|
68
|
+
# Apply fonts to the bullet to it displays consistently.
|
69
|
+
mix_font bullet_td, opt, ctx, parent_opts
|
70
|
+
|
71
|
+
html << bullet_td.to_s
|
72
|
+
|
73
|
+
# Number of non-breaking spaces to put before the bullet.
|
74
|
+
indent = (parent_opts[:indent] || 2).to_i
|
75
|
+
html << ' ' * indent if indent > 0
|
76
|
+
|
77
|
+
html << bullet
|
78
|
+
html << ' </td>'
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
# Start a new <td> to hold the list item itself.
|
83
|
+
item_td = Element.new('td')
|
84
|
+
|
85
|
+
# Consistent font handling for the items int he list.
|
86
|
+
mix_font item_td, opt, ctx, parent_opts
|
87
|
+
|
88
|
+
html << item_td.to_s
|
89
|
+
|
90
|
+
elsif tag == '/li'
|
91
|
+
|
92
|
+
# This space left intentionally blank. The closing {/td} is added either
|
93
|
+
# by the next child in the list (so that vertical space can be injected into
|
94
|
+
# /this/ list item) or by closing the list itself.
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
html
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -34,6 +34,9 @@ module Inkcite
|
|
34
34
|
# span assumes the exact dimensions of the image.
|
35
35
|
DIMENSIONS.each { |dim| img.style[dim] = px(opt[dim]) }
|
36
36
|
|
37
|
+
# Image borders on mobile
|
38
|
+
mix_border img, opt, ctx
|
39
|
+
|
37
40
|
mobile = opt[:mobile]
|
38
41
|
|
39
42
|
# For FILL-style mobile images, override the width. The height (in px)
|
@@ -30,7 +30,9 @@ module Inkcite
|
|
30
30
|
# Other mobile-specific properties
|
31
31
|
MOBILE_HEIGHT = :'mobile-height'
|
32
32
|
MOBILE_MAX_WIDTH = :'mobile-max-width'
|
33
|
+
MOBILE_NOWRAP = :'mobile-nowrap'
|
33
34
|
MOBILE_PADDING = :'mobile-padding'
|
35
|
+
MOBILE_WRAP = :'mobile-wrap'
|
34
36
|
MOBILE_WIDTH = :'mobile-width'
|
35
37
|
|
36
38
|
class Rule
|
@@ -0,0 +1,207 @@
|
|
1
|
+
module Inkcite
|
2
|
+
module Renderer
|
3
|
+
|
4
|
+
# @helper Slanted Edge
|
5
|
+
# @summary Bulletproof, responsive, sloped edges for modern email designs
|
6
|
+
#
|
7
|
+
# @description
|
8
|
+
# The {slant} Helper provides consistent, reliable sloped edges between sections of your email. You can customize the size, color and dimensions of the slant. It renders using CSS in modern email clients and has a VML fallback for Outlook.
|
9
|
+
#
|
10
|
+
# @warning Slants are not compatible with Outlook 2016 which renders a thin, white border around the slant.
|
11
|
+
# @warning Older email clients render angled CSS borders without anti-aliasing
|
12
|
+
#
|
13
|
+
# @credit
|
14
|
+
# Slanted edges based on @M_J_Robbins sloped edges for email concept.
|
15
|
+
# https://codepen.io/M_J_Robbins/pen/rpzLNx
|
16
|
+
#
|
17
|
+
# @usage
|
18
|
+
# {div width=600 padding=15 bgcolor=#009}
|
19
|
+
# ...
|
20
|
+
# {/div}
|
21
|
+
# {slant bgcolor=#009 color=#909 bottom right width=600 height=50}
|
22
|
+
# {div width=600 padding=15 bgcolor=#909}
|
23
|
+
# ...
|
24
|
+
# {/div}
|
25
|
+
class Slant < Responsive
|
26
|
+
def render tag, opt, ctx
|
27
|
+
|
28
|
+
html = ''
|
29
|
+
|
30
|
+
# @attribute no-fallback If present, disables VML fallback so the slant doesn't appear in Outlook 2007-2013.; alias no-vml
|
31
|
+
no_vml = opt[NO_FALLBACK] || opt[NO_VML]
|
32
|
+
|
33
|
+
# Check to see if VML is enabled for this email
|
34
|
+
include_fallback = ctx.vml_enabled? && !no_vml
|
35
|
+
|
36
|
+
# The first time the slant is used, we need to initialize the VML shapes
|
37
|
+
# and the responsive class that will be used to scale this.
|
38
|
+
if include_fallback
|
39
|
+
if ctx.once?(:slant)
|
40
|
+
|
41
|
+
# Notify the context that VML has been used in this email.
|
42
|
+
ctx.vml_used!
|
43
|
+
|
44
|
+
html << IF_OUTLOOK_LT_2016
|
45
|
+
|
46
|
+
# These need to be wrapped in a div with zero height otherwise unwanted whitespace will
|
47
|
+
# appear in the email where these shapes are initialized.
|
48
|
+
# https://litmus.com/community/discussions/538-vml-outlook-07-10-13-unwanted-20px-padding-at-the-bottom
|
49
|
+
html << '{div font-size=0 line-height=0}'
|
50
|
+
|
51
|
+
html << render_vml_triangle('stl', [ [0, 1], [1, 0], [0, 0] ])
|
52
|
+
html << render_vml_triangle('str', [ [0, 0], [1, 0], [1, 1] ])
|
53
|
+
html << render_vml_triangle('sbl', [ [0, 0], [1, 1], [0, 1] ])
|
54
|
+
html << render_vml_triangle('sbr', [ [0, 1], [1, 1], [1, 0] ])
|
55
|
+
|
56
|
+
html << '{/div}'
|
57
|
+
html << '{/if}'
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @attribute height The height of the slope in pixels; required
|
63
|
+
height = opt[:height].to_i
|
64
|
+
# @attribute width The width of the slope in pixels; required
|
65
|
+
width = opt[:width].to_i
|
66
|
+
if height <= 0 || width <= 0
|
67
|
+
ctx.error('Missing slant dimensions', opt)
|
68
|
+
return nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# Pre-calculate half the width and height of the slant.
|
72
|
+
half_height = (height / 2.0).round(0)
|
73
|
+
half_width = (width / 2.0).round(0)
|
74
|
+
|
75
|
+
# @attribute color The foreground color of the slanted edge.; default #234
|
76
|
+
color = hex(opt[:color] || '#234')
|
77
|
+
|
78
|
+
directions = [
|
79
|
+
(opt[:bottom] ? :bottom : :top),
|
80
|
+
(opt[:left] ? :left : :right)
|
81
|
+
]
|
82
|
+
|
83
|
+
border_colors = []
|
84
|
+
DIRECTIONS.each do |d|
|
85
|
+
next if d.nil?
|
86
|
+
border_colors << (directions.include?(d) ? color : TRANSPARENT)
|
87
|
+
end
|
88
|
+
|
89
|
+
# @attribute no-wrap If present, disables the table that wraps the slant. Use this if the slant is already in a container (e.g. table or div) that provides width and background color.
|
90
|
+
no_wrap = opt[NO_WRAP]
|
91
|
+
unless no_wrap
|
92
|
+
wrap_div = Element.new('table')
|
93
|
+
wrap_div[:width] = width
|
94
|
+
|
95
|
+
# @attribute bgcolor The color that appears behind the slanted edge; default transparent; alias background-color
|
96
|
+
bgcolor = detect_bgcolor(opt)
|
97
|
+
wrap_div[:bgcolor] = bgcolor unless none?(bgcolor)
|
98
|
+
|
99
|
+
wrap_div[:mobile] = :fill
|
100
|
+
html << wrap_div.to_helper
|
101
|
+
html << '{td}'
|
102
|
+
end
|
103
|
+
|
104
|
+
# This zero-sized div will have beefed up borders that create the
|
105
|
+
# slant effect.
|
106
|
+
slant_div = Element.new('div')
|
107
|
+
slant_div.style[BORDER_COLOR] = border_colors.join(' ')
|
108
|
+
slant_div.style[BORDER_STYLE] = :solid
|
109
|
+
slant_div.style[BORDER_WIDTH] = "#{px(half_height)} #{px(half_width)}"
|
110
|
+
slant_div.style[MSO_HIDE] = :all
|
111
|
+
|
112
|
+
# Fix to trigger antialiasing in the slanted border on webkit clients
|
113
|
+
# like Outlook for iOS
|
114
|
+
# https://stackoverflow.com/a/27506977
|
115
|
+
slant_div.style[:'-webkit-transform'] = 'rotate(0.0005deg)'
|
116
|
+
|
117
|
+
# Create a custom mobile class name for this slant. Then check to
|
118
|
+
# see if that responsive rule has already been registered - otherwise
|
119
|
+
# add it.
|
120
|
+
klass = "slant#{half_height}"
|
121
|
+
slant_rule = ctx.media_query.find_by_klass(klass) || Rule.new('div', klass, "border-width: #{px(half_height)} 50vw !important;")
|
122
|
+
ctx.media_query << slant_div.add_rule(slant_rule)
|
123
|
+
|
124
|
+
html << slant_div.to_s
|
125
|
+
html << '</div>'
|
126
|
+
|
127
|
+
if include_fallback
|
128
|
+
backward = false;
|
129
|
+
|
130
|
+
vshape = Element.new('v:shape')
|
131
|
+
|
132
|
+
# Generate the unique VML shape ID from the direction of the
|
133
|
+
# 90-degree corner.
|
134
|
+
type_id = "s" # Slant
|
135
|
+
type_id << (opt[:bottom] ? 'b' : 't')
|
136
|
+
type_id << (opt[:left] ? 'l' : 'r')
|
137
|
+
|
138
|
+
vshape[:type] = quote(type_id)
|
139
|
+
vshape.style[:width] = px(width)
|
140
|
+
vshape.style[:height] = px(height)
|
141
|
+
vshape.style[:'mso-position-horizontal'] = :center
|
142
|
+
vshape[:fillcolor] = color
|
143
|
+
vshape[:stroked] = :f
|
144
|
+
|
145
|
+
# Render the outlook-only VML-based fallback. Once again, it needs
|
146
|
+
# to be wrapped in a zero-height div to prevent unwanted space from
|
147
|
+
# being injected on certain version of outlook.
|
148
|
+
html << IF_OUTLOOK_LT_2016
|
149
|
+
html << '{div font-size=0 line-height=0}'
|
150
|
+
html << vshape.to_s
|
151
|
+
html << '<o:lock selection="t"/>'
|
152
|
+
html << '</v:shape>'
|
153
|
+
html << '{/div}'
|
154
|
+
html << '{/if}'
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
# Close the wrap element.
|
159
|
+
html << '{/td}{/table}' unless no_wrap
|
160
|
+
|
161
|
+
html
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def render_vml_coordinate coord
|
167
|
+
x, y = coord
|
168
|
+
"#{x * COORDINATE_SCALE},#{y * COORDINATE_SCALE}"
|
169
|
+
end
|
170
|
+
|
171
|
+
def render_vml_triangle id, coords
|
172
|
+
|
173
|
+
path = ''
|
174
|
+
path << 'm' # moveto
|
175
|
+
path << render_vml_coordinate(coords[0])
|
176
|
+
path << 'l' # lineto
|
177
|
+
path << render_vml_coordinate(coords[1])
|
178
|
+
path << ','
|
179
|
+
path << render_vml_coordinate(coords[2])
|
180
|
+
path << 'x' # close
|
181
|
+
path << 'e' # end
|
182
|
+
|
183
|
+
%Q(<v:shapetype id="#{id}" path="#{path}" xmlns:v="urn:schemas-microsoft-com:vml"/>)
|
184
|
+
end
|
185
|
+
|
186
|
+
# This is the MSO conditional used to prevent the slant from
|
187
|
+
# appearing in Outlook 2016 where it has thin lines around the
|
188
|
+
# VML according to Litmus results.
|
189
|
+
IF_OUTLOOK_LT_2016 = '{if test="lt mso 16"}'
|
190
|
+
|
191
|
+
# Flags allowing the user to disable VML fallbacks.
|
192
|
+
NO_FALLBACK = :'no-fallback'
|
193
|
+
NO_VML = :'no-vml'
|
194
|
+
|
195
|
+
# Tag allowing the user to disable the wrap element if they don't want it
|
196
|
+
NO_WRAP = :'no-wrap'
|
197
|
+
|
198
|
+
# Static constant for the color used where the border is transparent
|
199
|
+
# to create the slanted edge.
|
200
|
+
TRANSPARENT = 'transparent'
|
201
|
+
|
202
|
+
# Arbitrary multiplier present in the original codepen.
|
203
|
+
COORDINATE_SCALE = 21600
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -2,6 +2,10 @@ module Inkcite
|
|
2
2
|
module Renderer
|
3
3
|
class Snow < SpecialEffect
|
4
4
|
|
5
|
+
# Name of the array of value distribution used
|
6
|
+
# to make the animation start at different time points.
|
7
|
+
STARTING_PERCENT = :'starting-percent'
|
8
|
+
|
5
9
|
protected
|
6
10
|
|
7
11
|
def config_all_children style, sfx
|
@@ -23,7 +27,13 @@ module Inkcite
|
|
23
27
|
style[:opacity] = opacity if opacity < OPACITY_CEIL
|
24
28
|
|
25
29
|
animation.duration = speed
|
26
|
-
|
30
|
+
|
31
|
+
# If you start an animation with a negative delay, browsers and email clients
|
32
|
+
# start the animation as if it had already begun some time in the past
|
33
|
+
# https://css-tricks.com/starting-css-animations-mid-way/
|
34
|
+
starting_percent = sfx[STARTING_PERCENT][n] / 100.0
|
35
|
+
animation.delay = 0 - (speed * starting_percent).round
|
36
|
+
|
27
37
|
animation.timing_function = Animation::LINEAR
|
28
38
|
|
29
39
|
start_left = sfx.positions_x[n]
|
@@ -62,6 +72,10 @@ module Inkcite
|
|
62
72
|
# the container's width in random order.
|
63
73
|
sfx.positions_x.shuffle!
|
64
74
|
|
75
|
+
# Generate a random assortment of percent completed for the
|
76
|
+
# snowflakes so they can start mid-fall.
|
77
|
+
sfx[STARTING_PERCENT] = sfx.equal_distribution(0..100, sfx.count).shuffle
|
78
|
+
|
65
79
|
end
|
66
80
|
|
67
81
|
def defaults opt, ctx
|