class Inkcite::Renderer::Fireworks
Animated CSS fireworks special effect based on the technique by Eddie Lin codepen.io/yshlin/pen/ylDEk
Constants
- DECAY_ANIMATION_NAME
- DIAMETER_MAX
- DIAMETER_MIN
Names of the attributes controlling min and max explosion size.
- HUE_RANGE
The number of hue degrees to randomly alter the hue of each spark
- LUMINANCE
- SATURATION
Attributes used for color generation
Protected Instance Methods
config_all_children(style, sfx)
click to toggle source
# File lib/inkcite/renderer/fireworks.rb, line 147 def config_all_children style, sfx # If all of the sparks in the firework have the same size # (e.g. min-size equals max-size) then save some CSS space # by defining it once for all children. if sfx.same_size? style[:width] = px(sfx.min_size) style[:height] = px(sfx.min_size) end # Make sure all explosions start off-screen. style[:top] = "-#{px(sfx.max_size)}" style[BORDER_RADIUS] = '50%' sparks = sfx[:sparks].to_i # All sparks start with a box shadow at their exact center, # all in white. box_shadow = sparks.times.collect { |n| '0 0 #fff' } style[BOX_SHADOW] = box_shadow.join(', ') # Create the global decay animation that is consistent for all fireworks. # There is no variance in this animation so it is created and added to the # context only once. create_decay_animation(sfx) end
config_child(n, child, style, animation, sfx)
click to toggle source
# File lib/inkcite/renderer/fireworks.rb, line 176 def config_child n, child, style, animation, sfx # If all of the fireworks are different possible sizes # then pick a random size for this child. unless sfx.same_size? size = sfx.rand_size style[:width] = px(size) style[:height] = px(size) end # If rainbow is specified, choose the next color in the array - otherwise # choose a random hue unless a specific one has been specified. hue = sfx[:rainbow] ? sfx[:hues][n] : (sfx[:hue] || rand(360)).to_i # Randomly pick a color for this explosion by choosing a # random hue and then converting it to a hex color color = Inkcite::Util::hsl_to_color(hue, 100, 50) style[BACKGROUND_COLOR] = color # After the first child, each firework should have a random # delay before its animation starts - giving the different # fireworks a staggered launch. delay = n > 0 ? 0.25 + rand(sfx.count).round(2) : 0 # This is the total amount of time it will take the firework to # move through each of its positions. position_speed = sfx.rand_speed # This is the speed the firework animates it's explosion and decay # components - which need to repeat n-number of times based on the # total number of positions. explosion_speed = (position_speed / sfx[:stops].to_f).round(2) gravity_animation = Inkcite::Animation.new(DECAY_ANIMATION_NAME, sfx.ctx) gravity_animation.duration = explosion_speed gravity_animation.delay = delay if n > 0 gravity_animation.timing_function = Inkcite::Animation::EASE_IN_CUBIC composite_animation = Inkcite::Animation::Composite.new composite_animation << create_explosion_animation(n, hue, explosion_speed, delay, sfx) composite_animation << gravity_animation composite_animation << create_position_animation(n, position_speed, delay, sfx) style[:animation] = composite_animation end
defaults(opt, ctx)
click to toggle source
# File lib/inkcite/renderer/fireworks.rb, line 223 def defaults opt, ctx { :bgcolor => '#000000', :sparks => 50, :count => 2, :gravity => 200, DIAMETER_MIN => 25, DIAMETER_MAX => 200, SIZE_MIN => 10, SIZE_MAX => 10, SPEED_MIN => 5, SPEED_MAX => 10, :stops => 5, } end
Private Instance Methods
config_effect_context(sfx)
click to toggle source
# File lib/inkcite/renderer/fireworks.rb, line 21 def config_effect_context sfx count = sfx.count # Make sure the total number of stops (formerly TOTAL_POSITIONS) # is specified as an integer. sfx[:stops] = sfx[:stops].to_i # Total number of firework instances multiplied by the number # of positions each firework will cycle through. position_count = sfx[:stops] * count positions = sfx.equal_distribution(sfx.position_range, position_count) sfx[:x_positions] = positions sfx[:y_positions] = positions.dup # Equal distribution of hues based on the number of fireworks # if the rainbow option is selected sfx[:hues] = sfx.equal_distribution(0..360, count) end
create_decay_animation(sfx)
click to toggle source
# File lib/inkcite/renderer/fireworks.rb, line 89 def create_decay_animation sfx anim = Animation.new(DECAY_ANIMATION_NAME, sfx.ctx) # All fireworks fade to zero opacity and size by the end of the decay cycle. keyframe = anim.add_keyframe 100, { :opacity => 0, :width => 0, :height => 0 } # Check to see if gravity has been specified for the fireworks. If so # apply it as a vertical translation (positive equals downward) gravity = sfx[:gravity].to_i keyframe[:transform] = "translateY(#{px(gravity)})" if gravity != 0 sfx.animations << anim end
create_explosion_animation(n, hue, duration, delay, sfx)
click to toggle source
# File lib/inkcite/renderer/fireworks.rb, line 43 def create_explosion_animation n, hue, duration, delay, sfx # Calculate the radius size for this explosion min_diameter = sfx[DIAMETER_MIN].to_i max_diameter = sfx[DIAMETER_MAX].to_i diameter_range = (min_diameter..max_diameter) hue_range = (sfx[HUE_RANGE] || 40).to_i hue_range_2x = hue_range * 2 sparks = sfx[:sparks].to_i angle = 0 angle_step = 360 / sparks.to_f box_shadow = sparks.times.collect do |n| # Pick a random angle. angle_radians = angle * PI_OVER_180 angle += angle_step # Pick a random radius radius = rand(diameter_range) / 2.0 # Pick a random position for this spark to move to x = (radius * Math::cos(angle_radians)).round(0) y = (radius * Math::sin(angle_radians)).round(0) # Randomly pick a slightly different hue for this spark _hue = hue + hue_range - rand(hue_range_2x) color = Inkcite::Util::hsl_to_color(_hue, SATURATION, LUMINANCE) "#{px(x)} #{px(y)} #{color}" end anim = Inkcite::Animation.new(sfx.animation_class_name(n, 'bang'), sfx.ctx) anim.duration = duration anim.delay = delay if delay > 0 anim.timing_function = Inkcite::Animation::EASE_OUT_QUART anim.add_keyframe 100, { BOX_SHADOW => box_shadow.join(', ') } sfx.animations << anim anim end
create_position_animation(n, duration, delay, sfx)
click to toggle source
# File lib/inkcite/renderer/fireworks.rb, line 105 def create_position_animation n, duration, delay, sfx stops = sfx[:stops] anim = Inkcite::Animation.new(sfx.animation_class_name(n, 'position'), sfx.ctx) anim.duration = duration anim.delay = delay if delay > 0 anim.timing_function = Inkcite::Animation::LINEAR x_positions = sfx[:x_positions] y_positions = sfx[:y_positions] # This is the percentage amount of the total animation that will # be spent in each position. keyframe_duration = 100.0 / stops.to_f percent = 0 stops.times do |n| # Pick a random position for this firework top = y_positions.delete_at(rand(y_positions.length)) left = x_positions.delete_at(rand(x_positions.length)) # Calculate when the next keyframe will trigger. next_keyframe = percent + keyframe_duration # Calculate when this frame should end end_percent = n < stops - 1 ? (next_keyframe - 0.1).round(1) : 100 keyframe = anim.add_keyframe(percent.round(1), { :top => pct(top), :left => pct(left) }) keyframe.end_percent = end_percent percent = next_keyframe end sfx.animations << anim anim end