class Inkcite::Renderer::SpecialEffect
Constants
- OPACITY_CEIL
- OPACITY_MAX
- OPACITY_MIN
Opacity constraints on the children
- PI_OVER_180
For converting degrees to radians and back
- POSITION_CEIL
- POSITION_FLOOR
Position min and max preventing animated elements from leaving the bounds of the container.
- SIZE_MAX
- SIZE_MIN
Size constraints on the animated children.
- SPEED_MAX
- SPEED_MIN
Speed constraints on the children.
Public Instance Methods
# File lib/inkcite/renderer/special_effect.rb, line 229 def render tag, opt, ctx # If the closing tag was received (e.g. /snow) then close the wrapper # div that was rendered by the opening tag. return '</div>' if tag.start_with?('/') # Retrieve the special effects default values (times, number of units, etc.) _defaults = defaults(opt, ctx) # Create a special effects context that simplifies working with the # opts, defaults and manages the styles/classes necessary to animate # the special effect. sfx = EffectContext.new(tag, opt, ctx, _defaults) # Provide the extending class with an opportunity to configure the # effect context prior to any rendering. config_effect_context sfx html = [] styles = [] # If this is the first special effect to be included in the email # we need to disable the CSS animation from Gmail - which only # accepts part of its <styles> leading to unexpected whitespace. # By putting this invalid CSS into the <style> block, Gmail's # very strict parser will exclude the entire block, preventing # the animation from running. # https://emails.hteumeuleu.com/troubleshooting-gmails-responsive-design-support-ad124178bf81#.8jh1vn9mw if ctx.email? && sfx.uuid == 1 styles << Inkcite::Renderer::Style.new(".gmail-fix", sfx.ctx, { FONT_SIZE => '3*px' }) end # True if we're limiting the rendering of the animation to iOS clients only_ios = ctx.email? && !ctx.development? styles << '@media screen and (-webkit-min-device-pixel-ratio:0) {' if only_ios # Create the <div> that wraps the entire animation. create_wrap_element html, sfx # Create the Style that defines the look of the wrapping container create_wrap_style styles, sfx # Create the Style that is applied to all children in the animation. create_all_children_style styles, sfx # Now create each of the child elements (e.g. the snowflakes) that # will be animated in this effect. Each child is created and animated # at the same time. create_child_elements html, styles, sfx # Append all of the Keyframes to the end of the styles, now that # the individual children are configured. sfx.animations.each { |a| styles << a.to_keyframe_css } styles << '}' if only_ios # Push the completed list of styles into the context's stack. ctx.styles << styles.join("\n") html.join("\n") end
Protected Instance Methods
The extending class can override this method to perform any additional configuration on the style that affects all children in the animation.
# File lib/inkcite/renderer/special_effect.rb, line 319 def config_all_children style, sfx # This space left intentionally blank end
The extending class must implement this method to customize and animate each child.
# File lib/inkcite/renderer/special_effect.rb, line 325 def config_child n, child, style, animation, sfx raise 'Classes extending SpecialEffect must implement defaults(child, style, animation, keyframes, sfx)' end
The extending class can implement this method to customize the EffectContext
prior to any HTML or CSS generation.
# File lib/inkcite/renderer/special_effect.rb, line 331 def config_effect_context sfx # This space left intentionally blank end
The extending class can override this method to perform any additional configuration on the <div> that wraps the entire animation.
# File lib/inkcite/renderer/special_effect.rb, line 338 def config_wrap_element div, sfx # This space left intentionally blank end
The extending class can override this method to customize the wrap <div>'s style.
# File lib/inkcite/renderer/special_effect.rb, line 344 def config_wrap_style style, sfx # This space left intentionally blank end
The extending class must override this method and return the defaults for the special effect as a map.
# File lib/inkcite/renderer/special_effect.rb, line 350 def defaults opt, ctx raise 'Classes extending SpecialEffect must implement defaults(opt, ctx)' end
Private Instance Methods
Creates the Style
that applies to /all/ children.
# File lib/inkcite/renderer/special_effect.rb, line 357 def create_all_children_style styles, sfx style = Inkcite::Renderer::Style.new(".#{sfx.all_children_class_name}", sfx.ctx, { :position => :absolute }) # If no image has been provided, make the background a solid color # otherwise set the background to the image source and fill the # available space. src = sfx.src if src.blank? color = sfx.color style[BACKGROUND_COLOR] = color unless none?(color) else style[BACKGROUND_IMAGE] = "url(#{sfx.ctx.image_url(src.first)})" unless src.length > 1 style[BACKGROUND_SIZE] = '100%' end # Provide the extending class with a chance to apply additional # styles to all children. config_all_children style, sfx styles << style end
Creates n-number of child <div> objects and assigns the all-child and each-child CSS classes to them allowing each to be sized, animated uniquely.
# File lib/inkcite/renderer/special_effect.rb, line 383 def create_child_elements html, styles, sfx # Get a local handle on the src array in case we need to apply # a random one to each child (because there are multiple) src = sfx.src sfx.count.times do |n| child_class_name = sfx.child_class_name(n) # This is the child HTML element child = Inkcite::Renderer::Element.new('div', { :class => quote("#{sfx.all_children_class_name} #{child_class_name}") }) # This is the custom style to be applied to the child. style = Inkcite::Renderer::Style.new(".#{child_class_name}", sfx.ctx) # If there are multiple images, apply the next image based on the # child's index. if src.length > 1 src_index = n % src.length style[BACKGROUND_IMAGE] = "url(#{sfx.ctx.image_url(src[src_index])})" end # This is the animation declaration (timing, duration, etc.) for this child. animation = Inkcite::Animation.new(sfx.animation_class_name(n), sfx.ctx) # Provide the extending class with a chance to configure the child # and its style. config_child n, child, style, animation, sfx # Add the child's HTML element into the email. html << child.to_s + '</div>' # If the extending class actually defined an animation for this child # then assign it and add it to the list of animations to be appended # after the styles are injected. unless animation.blank? sfx.animations << animation # If the extending class didn't assign the animation already, then # assign it to the child's style - this adds itself with the appropriate # browser prefixes. style[:animation] = animation if style[:animation].blank? end # Append the child's custom style, unless blank (meaning the extending # class did not customize the child's styles directly). styles << style unless style.blank? end end
Creates the <div> that wraps the entire animation. The children of this container will be animated based on the parameters of the effect.
# File lib/inkcite/renderer/special_effect.rb, line 440 def create_wrap_element html, sfx # Initialize the wrap that will hold each of the children and wraps the content # over which the special effect will animate. div = Inkcite::Renderer::Element.new('div', { :class => quote(sfx.wrap_class_name) }) # Background color gets applied directly to the div so it renders consistently # in all clients - even those that don't support special effects. mix_background div, sfx.opt, sfx.ctx # Text alignment within the wrapper mix_text_align div, sfx.opt, sfx.ctx # Provide the extending class with a chance to make additional # configuration changes to the wrap element. config_wrap_element div, sfx html << div.to_s end
# File lib/inkcite/renderer/special_effect.rb, line 461 def create_wrap_style styles, sfx # Initialize the class declaration that will be applied to the # wrapping container. style = Inkcite::Renderer::Style.new(".#{sfx.wrap_class_name}", sfx.ctx, { :position => :relative, :overflow => :hidden, :width => '100%' }) # If a specific height has been specified for the wrap class, add # it to the style. height = sfx.height style[:height] = px(height) if height > 0 # Provide the extending class with a chance to do any additional # customization to this style. config_wrap_style style, sfx styles << style end