187 lines
5.5 kB
1
defmodule BlogWeb.RainbowLive do
2
use BlogWeb, :live_view
3
require Logger
4
5
@rainbow_colors [
6
"#FF0000", # Red
7
"#FF7F00", # Orange
8
"#FFFF00", # Yellow
9
"#00FF00", # Green
10
"#0000FF", # Blue
11
"#4B0082", # Indigo
12
"#9400D3" # Violet
13
]
14
@frame_interval 50 # 50ms between frames
15
@max_radius 300
16
@animation_steps 60
17
18
# Add DVD animation configuration
19
@dvd_speed 2
20
@viewport_width 600
21
@viewport_height 400
22
@logo_width 100
23
@logo_height 50
24
25
def mount(_params, _session, socket) do
26
if connected?(socket) do
27
Process.send_after(self(), :animate, @frame_interval)
28
end
29
30
{:ok,
31
assign(socket,
32
rainbows: [], # List of rainbow states
33
dvd_pos: %{ # Add DVD position state
34
x: Enum.random(-300..300), # Use integers instead of float division
35
y: Enum.random(-200..200), # Match the SVG viewBox dimensions
36
dx: @dvd_speed,
37
dy: @dvd_speed,
38
hue: 0
39
},
40
meta_attrs: [
41
%{name: "title", content: "Type shit and hear sounds and see wild shit or whatever"},
42
%{name: "description", content: "Bobby got high and made it so it looks wild when you press keys, theres web audio too but its broken."},
43
%{property: "og:title", content: "Type shit and hear sounds and see wild shit or whatever"},
44
%{property: "og:description", content: "Bobby got high and made it so it looks wild when you press keys, theres web audio too but its broken."},
45
%{property: "og:type", content: "website"}
46
],
47
page_title: "lol start typing and see what happens"
48
)}
49
end
50
51
def handle_event("keydown", %{"key" => key}, socket) when byte_size(key) == 1 do
52
# Create a new rainbow with random position
53
new_rainbow = %{
54
id: System.unique_integer([:positive]),
55
frame: 0,
56
x: Enum.random(-100..100), # Random x position
57
y: Enum.random(-50..50) # Random y position
58
}
59
60
{:noreply, assign(socket, rainbows: [new_rainbow | socket.assigns.rainbows])}
61
end
62
63
def handle_event("keydown", _key, socket), do: {:noreply, socket}
64
65
def handle_info(:animate, socket) do
66
Process.send_after(self(), :animate, @frame_interval)
67
68
# Update DVD position and handle bouncing
69
dvd_pos = update_dvd_position(socket.assigns.dvd_pos)
70
71
# Update rainbows (keep existing rainbow update logic)
72
updated_rainbows = socket.assigns.rainbows
73
|> Enum.map(fn rainbow ->
74
%{rainbow | frame: rainbow.frame + 1}
75
end)
76
|> Enum.reject(fn rainbow ->
77
rainbow.frame >= @animation_steps
78
end)
79
80
{:noreply, assign(socket,
81
rainbows: updated_rainbows,
82
dvd_pos: dvd_pos
83
)}
84
end
85
86
# Add DVD position update logic
87
defp update_dvd_position(pos) do
88
new_x = pos.x + pos.dx
89
new_y = pos.y + pos.dy
90
91
{dx, new_hue} = if new_x <= -(@viewport_width/2) + @logo_width or new_x >= (@viewport_width/2) - @logo_width do
92
{-pos.dx, rem(pos.hue + 60, 360)}
93
else
94
{pos.dx, pos.hue}
95
end
96
97
{dy, final_hue} = if new_y <= -(@viewport_height/2) + @logo_height or new_y >= (@viewport_height/2) - @logo_height do
98
{-pos.dy, rem(new_hue + 60, 360)}
99
else
100
{pos.dy, new_hue}
101
end
102
103
%{
104
x: new_x,
105
y: new_y,
106
dx: dx,
107
dy: dy,
108
hue: final_hue
109
}
110
end
111
112
defp calculate_arcs(frame) do
113
progress = frame / @animation_steps
114
115
@rainbow_colors
116
|> Enum.with_index()
117
|> Enum.map(fn {color, index} ->
118
radius = @max_radius - (index * 40)
119
arc_progress = min(1.0, progress * 1.2 - (index * 0.1))
120
121
if arc_progress > 0 do
122
generate_arc_path(radius, arc_progress, color)
123
end
124
end)
125
|> Enum.reject(&is_nil/1)
126
end
127
128
defp generate_arc_path(radius, progress, color) do
129
end_angle = :math.pi * progress
130
end_x = radius * :math.cos(end_angle)
131
end_y = radius * :math.sin(end_angle)
132
133
path = "M #{radius} 0 A #{radius} #{radius} 0 0 1 #{end_x} #{end_y}"
134
135
%{
136
path: path,
137
color: color,
138
stroke_width: 20
139
}
140
end
141
142
def render(assigns) do
143
~H"""
144
<div class="flex justify-center items-center min-h-screen bg-gray-900" phx-window-keydown="keydown">
145
<svg width="100%" height="100vh" viewBox="-300 -200 600 400">
146
<%!-- DVD Logo --%>
147
<g transform={"translate(#{@dvd_pos.x}, #{@dvd_pos.y})"}>
148
<path
149
d="M-50,-25 h100 v50 h-100 z M-30,-15 L-10,15 H10 L30,-15 H-30 Z M-20,0 h40 M-25,-10 h50"
150
fill={"hsl(#{@dvd_pos.hue}, 100%, 70%)"}
151
style="transform-origin: center; transform: scale(0.8);"
152
>
153
<animate
154
attributeName="opacity"
155
values="0.8;1;0.8"
156
dur="2s"
157
repeatCount="indefinite"
158
/>
159
</path>
160
<text
161
x="0"
162
y="5"
163
text-anchor="middle"
164
fill="white"
165
font-family="Arial Black"
166
font-size="20"
167
>DVD</text>
168
</g>
169
170
<%!-- Keep existing rainbow rendering --%>
171
<%= for rainbow <- @rainbows do %>
172
<g transform={"translate(#{rainbow.x}, #{rainbow.y})"}>
173
<%= for arc <- calculate_arcs(rainbow.frame) do %>
174
<path
175
d={arc.path}
176
stroke={arc.color}
177
stroke-width={arc.stroke_width}
178
fill="none"
179
/>
180
<% end %>
181
</g>
182
<% end %>
183
</svg>
184
</div>
185
"""
186
end
187
end
188