dancing-dots
This commit is contained in:
49
elixir/dancing-dots/lib/dancing_dots/animation.ex
Normal file
49
elixir/dancing-dots/lib/dancing_dots/animation.ex
Normal file
@@ -0,0 +1,49 @@
|
||||
defmodule DancingDots.Animation do
|
||||
@type dot :: DancingDots.Dot.t()
|
||||
@type opts :: keyword
|
||||
@type error :: any
|
||||
@type frame_number :: pos_integer
|
||||
|
||||
@callback init(opts) :: {:ok, opts} | {:error, error}
|
||||
@callback handle_frame(dot, frame_number, opts) :: dot
|
||||
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
@behaviour DancingDots.Animation
|
||||
def init(opts), do: {:ok, opts}
|
||||
defoverridable init: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule DancingDots.Flicker do
|
||||
use DancingDots.Animation
|
||||
|
||||
@impl DancingDots.Animation
|
||||
def handle_frame(%DancingDots.Dot{opacity: opacity} = dot, frame_number, _opts)
|
||||
when rem(frame_number, 4) == 0,
|
||||
do: %{dot | opacity: opacity / 2}
|
||||
|
||||
@impl DancingDots.Animation
|
||||
def handle_frame(dot, _frame_number, _opts), do: dot
|
||||
end
|
||||
|
||||
defmodule DancingDots.Zoom do
|
||||
use DancingDots.Animation
|
||||
|
||||
@impl DancingDots.Animation
|
||||
def init(opts) do
|
||||
case Keyword.get(opts, :velocity) do
|
||||
velocity when is_number(velocity) ->
|
||||
{:ok, opts}
|
||||
|
||||
velocity ->
|
||||
{:error,
|
||||
"The :velocity option is required, and its value must be a number. Got: #{inspect(velocity)}"}
|
||||
end
|
||||
end
|
||||
|
||||
@impl DancingDots.Animation
|
||||
def handle_frame(%DancingDots.Dot{radius: radius} = dot, frame_number, opts),
|
||||
do: %{dot | radius: radius + (frame_number - 1) * Keyword.get(opts, :velocity)}
|
||||
end
|
||||
4
elixir/dancing-dots/lib/dancing_dots/dot.ex
Normal file
4
elixir/dancing-dots/lib/dancing_dots/dot.ex
Normal file
@@ -0,0 +1,4 @@
|
||||
defmodule DancingDots.Dot do
|
||||
defstruct [:x, :y, :radius, :opacity]
|
||||
@type t :: %__MODULE__{}
|
||||
end
|
||||
61
elixir/dancing-dots/lib/dancing_dots/dot_group.ex
Normal file
61
elixir/dancing-dots/lib/dancing_dots/dot_group.ex
Normal file
@@ -0,0 +1,61 @@
|
||||
defmodule DancingDots.DotGroup do
|
||||
# This module is an example of how behaviours can be used in practice.
|
||||
# You don't need to read it to solve this exercise.
|
||||
# It's here for the curious :)
|
||||
|
||||
@moduledoc """
|
||||
Allows applying a list of one or more animations to a group of one or more dots.
|
||||
"""
|
||||
|
||||
defstruct [:dots, :animations_with_opts]
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
@doc """
|
||||
Creates a new dot group with given dots and an empty list of animations.
|
||||
"""
|
||||
@spec new([DancingDots.Dot.t()]) :: t()
|
||||
def new(dots) do
|
||||
%__MODULE__{
|
||||
dots: dots,
|
||||
animations_with_opts: []
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Validates the given animation module with its given options and adds it to the group.
|
||||
"""
|
||||
@spec add_animation(t(), module, DancingDots.Animation.opts()) ::
|
||||
{:ok, t()} | {:error, DancingDots.Animation.error()}
|
||||
def add_animation(dot_group, animation_module, opts) do
|
||||
# using Animation's init/1 callback
|
||||
init_result = animation_module.init(opts)
|
||||
|
||||
case init_result do
|
||||
{:ok, opts} ->
|
||||
animations_with_opts = [{animation_module, opts} | dot_group.animations_with_opts]
|
||||
dot_group = %{dot_group | animations_with_opts: animations_with_opts}
|
||||
{:ok, dot_group}
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Applies the list of animations to all the dots.
|
||||
"""
|
||||
@spec render_dots(t(), DancingDots.Animation.frame_number()) :: [DancingDots.Dot.t()]
|
||||
def render_dots(dot_group, frame_number) do
|
||||
%{
|
||||
dots: dots,
|
||||
animations_with_opts: animations_with_opts
|
||||
} = dot_group
|
||||
|
||||
Enum.map(dots, fn dot ->
|
||||
Enum.reduce(animations_with_opts, dot, fn {animation_module, opts}, acc ->
|
||||
# using Animation's handle_frame/3 callback
|
||||
animation_module.handle_frame(acc, frame_number, opts)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user