git.iko.soy/rowtype-yoga/psfp
on acfd041 help sign in
954 lines 31 kB history raw
1module SVG.Image where
3import Prelude
4import CSS.Safer (cssSafer)
5import DOM.Hook (useBoundingBox)
6import Data.Array (elem, foldl, head, intercalate, snoc, uncons, zip, (:))
7import Data.Int (round, toNumber)
8import Data.Maybe (Maybe(..), maybe)
9import Data.String (codePointFromChar)
10import Data.String as String
11import Data.Tuple.Nested ((/\))
12import Effect (Effect)
13import Random.PseudoRandom (mkSeed, randomRs)
14import React.Basic (JSX, element)
15import React.Basic.DOM (unsafeCreateDOMComponent)
16import React.Basic.DOM as HTML
17import React.Basic.DOM.SVG as SVG
18import React.Basic.Hooks (ReactComponent)
19import React.Basic.Hooks as React
20import SVG.Icon (Raw)
21import Scroll.Hook (useScrollYPosition)
22import Theme.Styles (makeStyles, useTheme)
23import Theme.Types (CSSTheme)
25foreign import landingPageDarkRaw ∷ ∀ r. Raw r
27foreign import landingPageLightRaw ∷ ∀ r. Raw r
29mkLandingPageBackground ∷ Effect (ReactComponent { className ∷ String })
30mkLandingPageBackground = do
31 useStyles <-
32 makeStyles \(theme ∷ CSSTheme) ->
33 { laptopBackground:
34 cssSafer
35 { fill: theme.backgroundColour
36 }
37 , lightEllipsis:
38 cssSafer
39 { cx: if theme.isLight then "721" else "175"
40 , cy: if theme.isLight then "355" else "310"
41 , rx: if theme.isLight then "647" else "330"
42 , ry: if theme.isLight then "310" else "252"
43 }
44 , laptopBody:
45 cssSafer
46 { fill: if theme.isLight then "url(#_Linear9)" else "url(#_Linear12)"
47 }
48 , screenText:
49 cssSafer
50 { fontFamily: "PragmataPro"
51 , fontSize: "0.85em"
52 , fill: theme.textColour
53 }
54 , keyword: cssSafer { fill: theme.green }
55 , typeName: cssSafer { fill: theme.pink }
56 , "@keyframes animatedCursor":
57 cssSafer
58 { "0%": { opacity: "0.0" }
59 , "50%": { opacity: "1.0" }
60 }
61 , cursor:
62 cssSafer
63 { animation: "$animatedCursor 0.5s linear infinite"
64 , animationDirection: "alternate"
65 }
66 , "@keyframes movingCloud1":
67 cssSafer
68 { "0%": { transform: "translate3d(-140%, 4%, 0)" }
69 , "100%": { transform: "translate3d(70%, -4%, 0)" }
70 }
71 , movingCloud1:
72 cssSafer
73 { animation: "$movingCloud1 200s linear infinite"
74 , animationDelay: "-150s"
75 }
76 , "@keyframes movingCloud2":
77 cssSafer
78 { "0%": { transform: "translate3d(-10%, 0%, 0)" }
79 , "100%": { transform: "translate3d(10%, 0%, 0)" }
80 }
81 , movingCloud2:
82 cssSafer
83 { animation: "$movingCloud2 120s linear infinite"
84 , animationDirection: "alternate"
85 }
86 , movingCloud3:
87 cssSafer
88 { animation: "$movingCloud2 120s linear infinite"
89 , animationDelay: "-120s"
90 , animationDirection: "alternate"
91 }
92 , "@keyframes twinkle":
93 cssSafer
94 { "0%": { fill: "rgba(250, 250, 250, 0.2)" }
95 , "50%": { fill: "rgba(250, 250, 250, 0.7)" }
96 , "70%": { fill: "rgba(250, 250, 250, 0.4)" }
97 , "100%": { fill: "rgba(250, 250, 250, 1.0)" }
98 }
99 , "@keyframes twinkleFast":
100 cssSafer
101 { "0%": { fill: "rgba(250, 250, 250, 0.7)" }
102 , "100%": { fill: "rgba(250, 250, 250, 0.78)" }
103 }
104 , twinklingStar1:
105 cssSafer
106 { animation: "$twinkleFast 0.2s ease-in-out infinite"
107 , animationDirection: "alternate"
108 }
109 , twinklingStar2:
110 cssSafer
111 { animation: "$twinkle 8s ease-in-out infinite"
112 , animationDelay: "4s"
113 , animationDirection: "alternate"
114 }
115 , twinklingStar3:
116 cssSafer
117 { animation: "$twinkle 4s ease-in-out infinite"
118 , animationDelay: "3s"
119 , animationDirection: "alternate"
120 }
121 , "@keyframes rotateStars":
122 cssSafer
123 { "0%": { transform: "rotate(0deg) scale(1,1)" }
124 , "100%": { transform: "rotate(1deg) scale(1.01, 1.01)" }
125 }
126 , stars:
127 cssSafer
128 { animation: "$rotateStars 30s linear infinite"
129 , transformOrigin: "top center"
130 , animationDirection: "alternate"
131 }
132 }
133 React.component "LandingPageBackground" \{ className } -> React.do
134 theme <- useTheme
135 classes <- useStyles
136 scrollY <- useScrollYPosition
137 bb /\ ref <- useBoundingBox
138 let
139 scrolled = case bb of
140 Nothing -> 0.0
141 Just vh -> scrollY / vh.height
142 pure
143 $ element (unsafeCreateDOMComponent "svg") -- [TODO] make PR for react-basic to allow for `ref` on svg
144 { ref
145 , viewBox: "0 0 800 447"
146 , preserveAspectRatio: "xMidYMax slice"
147 , xmlns: "http://www.w3.org/2000/svg"
148 , fillRule: "evenodd"
149 , clipRule: "evenodd"
150 , strokeLinejoin: "round"
151 , strokeMiterlimit: "2"
152 , className
153 , children:
154 [ SVG.path
155 { fill: "none"
156 , d: "M0 0h800v447H0z"
157 }
158 , SVG.clipPath
159 { id: "a"
160 , children:
161 [ SVG.path
162 { d: "M0 0h800v447H0z"
163 }
164 ]
165 }
166 , SVG.g
167 { clipPath: "url(#a)"
168 , children:
169 [ SVG.path
170 { d: "M800.828 448.051V-3.513H-3.028v451.564h803.856z"
171 , fill: "url(#_Linear2)"
172 }
173 , SVG.ellipse
174 { className: classes.lightEllipsis
175 , fill: "url(#_Radial3)"
176 }
177 , stars classes.stars classes
178 -- bgcloud
179 , SVG.path
180 { d: "M610.291 177.316c-35.879-.428-84.67-.691-138.392-.689-38.684.002-74.811.141-105.402.38-49.14-2.745-80.877-9.954-80.877-14.718 0-1.333 2.655.335 7.294-.896 96.772-.175 171.302-1.213 171.302-2.464 0-1.107-59.462-3.372-140.106-3.687 38.487-2.059 102.543-4.972 173.823-4.972 11.996 0 23.761.07 35.216.206 20.76-4.638 60.945-2.784 107.07-2.784 36.08 0 68.525-.075 90.882 2.983 32.491.899 72.142-1.186 114.922-.379 38.68.729 74.805 1.273 105.395 1.613 49.101 3.673 80.599 8.516 80.54 13.279-.016 1.334-2.504 2.58-7.158 3.723-96.763-1.654-171.297-2.024-171.312-.774-.013 1.108 58.473 3.15 139.105 4.989-38.514 3.085-101.653 4.38-172.925 3.033-11.995-.227-23.757-.52-35.209-.872-20.815 4.245-61.034 6.631-107.154 5.759-24.754-.468-47.786-1.809-67.014-3.73z"
181 , fill: if theme.isLight then "#ddf0fb" else "#240c3c"
182 , className: classes.movingCloud1
183 }
184 -- bgcloud
185 , SVG.path
186 { d: "M609.299 171.607c-35.88-.338-84.67-.545-138.392-.543-38.684.001-74.812.111-105.402.299-49.141-2.166-80.694-5.519-80.694-9.279 0-1.052 2.472-2.073 7.111-3.045 96.772-.137 171.302-.957 171.302-1.944-.001-.874-58.506-1.614-139.15-1.862 38.471-3.009 101.587-4.973 172.867-4.973 11.996 0 23.761.055 35.216.162 20.76-3.66 60.945-6.143 107.07-6.143 36.08 0 68.525 1.519 90.882 3.933 32.491.709 72.142 1.432 114.922 2.069 38.68.575 74.805 1.005 105.395 1.272 49.101 2.9 80.598 6.722 80.54 10.482-.017 1.052-2.505 2.036-7.158 2.938-96.763-1.305-171.297-1.597-171.312-.611-.014.875 58.473 2.486 139.104 3.938-38.514 2.435-101.653 3.457-172.924 2.394a3771.84 3771.84 0 01-35.21-.688c-20.814 3.35-61.033 5.234-107.153 4.545-24.754-.369-47.786-1.428-67.014-2.944z"
187 , fill: if theme.isLight then "#d8effd" else "#120b38"
188 , className: classes.movingCloud1
189 }
190 -- rightcloud
191 , SVG.path
192 { d: "M684.234 222.269c-12.108-1.642-29.721-2.832-29.76-5.218-.063-3.861 34.226-4.225 76.6-3.504 42.375.721 75.9-2.707 76.956 1.008 2.476 8.704-12.549 8.552-32.56 10.141-12.403-.58-28.24.216-44.434.479-16.193.263-35.197-1.331-46.802-2.906z"
193 , fill: if theme.isLight then "#d8effd" else "url(#_Linear4)"
194 , className: classes.movingCloud2
195 }
196 -- leftcloud
197 , SVG.path
198 { d: "M130.064 213.821c42.381 0 76.789.237 76.789 4.224 0 3.986-14.408 4.223-56.789 4.223-42.38 0-96.788.208-96.788-4.223s34.408-4.224 76.788-4.224z"
199 , fill: if theme.isLight then "#d8effd" else "#a7847d"
200 , className: classes.movingCloud3
201 }
202 , mountains theme.isLight scrolled
203 --buildings
204 , buildings theme.isLight scrolled
205 -- bar
206 , SVG.path
207 { fill: theme.backgroundColour
208 , d: "M-3.028 431.298h826.844v20.006H-3.028z"
209 }
210 -- laptop
211 , laptop classes scrolled
212 ]
213 }
214 , defs theme.isLight
215 ]
216 }
218laptop ∷
219 ∀ r.
220 { cursor ∷ String
221 , keyword ∷ String
222 , laptopBackground ∷ String
223 , laptopBody ∷ String
224 , screenText ∷ String
225 , typeName ∷ String
226 | r
227 } ->
228 Number -> JSX
229laptop classes scrolled =
230 SVG.g
231 { children:
232 [ SVG.path
233 { d: "M606.064 273.603c0-6.261-5.076-11.337-11.337-11.337H399.673c-6.261 0-11.337 5.076-11.337 11.337v151.521a5.001 5.001 0 005 5h207.728a5.004 5.004 0 003.536-1.464 5.004 5.004 0 001.464-3.536V273.603z"
234 , fill: "#657390"
235 }
236 , SVG.path
237 { d: "M606.064 273.603c0-6.261-5.076-11.337-11.337-11.337H399.673c-6.261 0-11.337 5.076-11.337 11.337v151.521a5.001 5.001 0 005 5h207.728a5.004 5.004 0 003.536-1.464 5.004 5.004 0 001.464-3.536V273.603z"
238 , fill: "#657390"
239 }
240 , SVG.path
241 { d: "M599.193 275.905a5.528 5.528 0 00-5.526-5.526H400.604a5.528 5.528 0 00-5.526 5.526v141.039a5.528 5.528 0 005.526 5.526h193.063a5.528 5.528 0 005.526-5.526V275.905z"
242 , fill: "#130c2b"
243 , className: classes.laptopBackground
244 }
245 , SVG.path
246 { d: "M655.04 427.228a.458.458 0 00-.457-.457H340.83v4.975a5 5 0 005 5h304.21a5 5 0 005-5v-4.518z"
247 , className: classes.laptopBody
248 }
249 , SVG.circle
250 { cx: "497.178"
251 , cy: "267.001"
252 , r: "1.238"
253 , fillOpacity: ".45"
254 }
255 , SVG.circle
256 { cx: "500.009"
257 , cy: "267.001"
258 , r: ".527"
259 , fillOpacity: ".45"
260 }
261 , SVG.circle
262 { cx: "494.263"
263 , cy: "267.001"
264 , r: ".527"
265 , fill: "#102708"
266 , fillOpacity: ".45"
267 }
268 , SVG.text
269 { x: "407"
270 , y: "290"
271 , className: classes.screenText
272 , children:
273 textToTspans classes scrolled 407.0
274 $ intercalate "\n"
275 [ "hoistJoker "
276 , "\t∷ ∀ f g a b"
277 , "\t. (f ~> g)"
278 , "\t-> Joker f a b"
279 , "\t-> Joker g a b"
280 , "hoistJoker f (Joker a) ="
281 , "\t Joker (f a)"
282 ]
283 }
284 ]
285 }
287buildings ∷ Boolean -> Number -> JSX
288buildings isLight scrolled =
289 SVG.g
290 { transform: "translate(0 " <> show (scrolled * 30.0) <> ")"
291 , children:
292 [ SVG.path -- 2 main
293 { d: "M195.171 451.298V159.153H92.709v292.145h102.462z"
294 , fill: if isLight then "url(#_Linear4)" else "url(#_Linear5)"
295 }
296 , SVG.path -- 4 main
297 { d: "M289.755 451.298V232.811h-57.232v218.487h57.232z"
298 , fill: if isLight then "url(#_Linear5)" else "url(#_Linear6)"
299 }
300 , SVG.path -- 4 shade
301 { d: "M207.557 451.298V232.811h25.314v218.487h-25.314zM92.709 451.298V159.153h25.314v292.145H92.709z"
302 , fill: if isLight then "#b4dbfb" else "#130425"
303 }
304 , SVG.path -- 5 shade
305 { d: "M323.451 451.298V327.713h-71.555v123.585h71.555z"
306 , fill: if isLight then "url(#_Linear6)" else "url(#_Linear7)"
307 , transform: "translate(0 " <> show (scrolled * 60.0) <> ")"
308 }
309 , SVG.path -- 5 main
310 { d: "M323.451 451.298V327.713h-16.692v123.585h16.692z"
311 , fill: if isLight then "url(#_Linear7)" else "url(#_Linear8)"
312 , transform: "translate(0 " <> show (scrolled * 60.0) <> ")"
313 }
314 , SVG.path -- 3 main
315 { d: "M272.498 451.298v-95.771H135.943v95.771h136.555z"
316 , fill: if isLight then "url(#_Linear8)" else "url(#_Linear9)"
317 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
318 }
319 , SVG.path -- 3 shade
320 { d: "M173.526 451.298v-95.771h-37.583v95.771h37.583z"
321 , fill: if isLight then "#b4dbfb" else "#2e1232"
322 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
323 }
324 , SVG.path -- 1 main
325 { d: "M85.122 451.298V272.442h-97.675v178.856h97.675z"
326 , fill: if isLight then "#b4dbfb" else "url(#_Linear10)"
327 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
328 }
329 , SVG.path -- 1 shade
330 { d: "M66.649 456.944V272.442H-3.028v184.502h69.677z"
331 , fill: if isLight then "#b4dbfb" else "url(#_Linear11)"
332 , transform: "translate(0 " <> show (scrolled * 100.0) <> ")"
333 }
334 ]
335 }
337mountains ∷ Boolean -> Number -> JSX
338mountains isLight scrolled =
339 SVG.g
340 { transform: "translate(0 " <> show (scrolled * 70.0) <> ")"
341 , children:
342 [ SVG.path
343 { d: "M476.098 218.299l81.644 216.765H352.334l123.764-216.765z"
344 , fill: if isLight then "#4560a0" else "#04102d"
345 }
346 , SVG.path
347 { d: "M476.098 218.299l21.823 203.477 81.177-8.706-103-194.771z"
348 , fill: if isLight then "#5e84ba" else "#250849"
349 }
350 -- 2 dark (left)
351 , SVG.path
352 { d: "M369.627 308.648l88.326 125.788H252.546l117.081-125.788z"
353 , fill: if isLight then "#4560a0" else "#04102d"
354 }
355 -- 1 dark (left)
356 , SVG.path
357 { d: "M326.11 338.332l68.193 96.732H188.896l137.214-96.732zM508.168 243.792l163.526 204.747H415.051l93.117-204.747z"
358 , fill: if isLight then "#4560a0" else "#04102d"
359 }
360 , SVG.path -- 1 and 2 light (right)
361 { d: "M508.168 245.355l63.395 75.115-54.953 46.379-8.442-121.494zM369.612 308.648l54.182 77.687-58.981 41.135 4.799-118.822zM326.184 338.326l16.813 23.99-16.887 11.835.074-35.825z"
362 , fill: if isLight then "#5e84ba" else "#250849"
363 }
364 -- right mountain
365 , SVG.path -- rightmost dark (left)
366 { d: "M633.647 230.42L809.34 435.064H457.953L633.647 230.42z"
367 , fill: if isLight then "#4560a0" else "#04102d"
368 }
369 , SVG.path -- rightmost light (right)
370 { d: "M633.647 230.42L735.22 475.549l86.041-35.653L633.647 230.42z"
371 , fill: if isLight then "#5e84ba" else "#250849"
372 }
373 , SVG.path -- peak left
374 { d: "M643.625 254.384c-.571.219-.745.283-1.863.82-2.864 1.395-6.079 3.209-9.184 2.64-6.187-1.134-10.854-6.283-16.725-6.738l17.786-20.729 9.986 24.007z"
375 , fill: if isLight then "#d9d9d9" else "#655578"
376 }
377 , SVG.path -- peak right
378 { d: "M654.229 253.4c-.494-.028-.875-.013-.875-.013-3.255-.486-6.617-.192-9.677.982l-.052.02-9.978-23.97 20.582 22.981z"
379 , fill: if isLight then "#fff" else "#9d779e"
380 }
381 ]
382 }
384textToTspans ∷
385 ∀ r.
386 { cursor ∷ String
387 , keyword ∷ String
388 , typeName ∷ String
389 | r
390 } ->
391 Number -> Number -> String -> Array JSX
392textToTspans classes scrolled x str = (foldl toTspan { distance: 0.0, acc: [] } lines).acc <> [ cursor ]
393 where
394 toTspan { distance, acc } line = case uncons (words line) of
395 Nothing -> { distance: distance + 1.2, acc }
396 Just { head: "", tail: [] } -> { distance: distance + 1.2, acc }
397 Just { head, tail } -> do
398 let
399 newElems ∷ Array JSX
400 newElems = _.spans $ foldl mkNewElems { prefix: " ", spans: [] } tail
402 mkNewElems { prefix, spans } = case _ of
403 "" -> { prefix: prefix <> " ", spans }
404 word ->
405 { prefix: " "
406 , spans:
407 spans
408 `snoc`
409 SVG.tspan
410 { children: [ HTML.text $ prefix <> word ]
411 , className: classNameForWord word
412 }
413 }
415 first =
416 SVG.tspan
417 { x: show x
418 , dy: show distance <> "em"
419 , children: [ HTML.text head ]
420 }
422 pimped ∷ Array JSX
423 pimped = first : newElems
424 { distance: 1.2, acc: acc <> pimped }
426 classNameForWord = case _ of
427 keyword
428 | elem keyword keywords -> classes.keyword
429 typeName
430 | isTypeName typeName -> classes.typeName
431 other -> ""
433 isTypeName s =
434 let
435 firstLetter = String.take 1 s
437 inRange c1 c2 = between (codePointFromChar c1) (codePointFromChar c2)
439 isLetter =
440 String.toCodePointArray
441 >>> head
442 >>> maybe false (inRange 'a' 'z' || inRange 'A' 'Z')
443 in
444 String.toUpper firstLetter == firstLetter && isLetter firstLetter
446 keywords =
447 [ "module"
448 , "∷"
449 , "::"
450 , "forall"
451 , "∀"
452 , "do"
453 , "where"
454 , "let"
455 , "in"
456 , "instance"
457 , "class"
458 , "data"
459 , "="
460 , "|"
461 , "type"
462 , "newtype"
463 , "derive"
464 , "if"
465 , "then"
466 , "else"
467 , "import"
468 ]
470 words line = split
471 where
472 split = String.split (String.Pattern " ") line
474 lines = String.split (String.Pattern "\n") scrolledString
476 scrolledString = String.take charactersToTake str
477 where
478 doneAfterXPercent = 0.5 -- [WARN] Don't set to 0
480 charactersToTake = round $ scrolled * (1.0 / doneAfterXPercent) * len
482 len = toNumber $ String.length str
484 cursor =
485 SVG.tspan
486 { dx: "0"
487 , dy: "0"
488 , children: [ HTML.text "█" ]
489 , className: classes.cursor
490 , id: "landing-page-cursor"
491 }
493defs ∷ Boolean -> JSX
494defs = if _ then lightDefs else darkDefs
496lightDefs ∷ JSX
497lightDefs =
498 SVG.defs
499 { children:
500 [ SVG.linearGradient
501 { id: "_Linear2"
502 , x1: "0"
503 , y1: "0"
504 , x2: "1"
505 , y2: "0"
506 , gradientUnits: "userSpaceOnUse"
507 , gradientTransform: "matrix(0 451.564 -803.856 0 398.9 -3.513)"
508 , children:
509 [ SVG.stop
510 { offset: "0"
511 , stopColor: "#68adfc"
512 }
513 , SVG.stop
514 { offset: ".34"
515 , stopColor: "#cbe0f8"
516 }
517 , SVG.stop
518 { offset: "1"
519 , stopColor: "#d2cbf8"
520 }
521 ]
522 }
523 , SVG.linearGradient
524 { id: "_Linear4"
525 , x1: "0"
526 , y1: "0"
527 , x2: "1"
528 , y2: "0"
529 , gradientUnits: "userSpaceOnUse"
530 , gradientTransform: "scale(293.8304) rotate(83.86 -.11 .484)"
531 , children:
532 [ SVG.stop
533 { offset: "0"
534 , stopColor: "#bde8f7"
535 }
536 , SVG.stop
537 { offset: "1"
538 , stopColor: "#86b6fe"
539 }
540 ]
541 }
542 , SVG.linearGradient
543 { id: "_Linear5"
544 , x1: "0"
545 , y1: "0"
546 , x2: "1"
547 , y2: "0"
548 , gradientUnits: "userSpaceOnUse"
549 , gradientTransform: "scale(219.3256) rotate(84.988 -.028 1.133)"
550 , children:
551 [ SVG.stop
552 { offset: "0"
553 , stopColor: "#bde8f7"
554 }
555 , SVG.stop
556 { offset: "1"
557 , stopColor: "#86b6fe"
558 }
559 ]
560 }
561 , SVG.linearGradient
562 { id: "_Linear6"
563 , x1: "0"
564 , y1: "0"
565 , x2: "1"
566 , y2: "0"
567 , gradientUnits: "userSpaceOnUse"
568 , gradientTransform: "rotate(90 -6.304 321.409) scale(123.585)"
569 , children:
570 [ SVG.stop
571 { offset: "0"
572 , stopColor: "#bde8f7"
573 }
574 , SVG.stop
575 { offset: "1"
576 , stopColor: "#86b6fe"
577 }
578 ]
579 }
580 , SVG.linearGradient
581 { id: "_Linear7"
582 , x1: "0"
583 , y1: "0"
584 , x2: "1"
585 , y2: "0"
586 , gradientUnits: "userSpaceOnUse"
587 , gradientTransform: "scale(132.4342) rotate(68.9 -1.112 2.415)"
588 , children:
589 [ SVG.stop
590 { offset: "0"
591 , stopColor: "#bde8f7"
592 }
593 , SVG.stop
594 { offset: "1"
595 , stopColor: "#86b6fe"
596 }
597 ]
598 }
599 , SVG.linearGradient
600 { id: "_Linear8"
601 , x1: "0"
602 , y1: "0"
603 , x2: "1"
604 , y2: "0"
605 , gradientUnits: "userSpaceOnUse"
606 , gradientTransform: "rotate(90 -88.788 166.635) scale(190.951)"
607 , children:
608 [ SVG.stop
609 { offset: "0"
610 , stopColor: "#bde8f7"
611 }
612 , SVG.stop
613 { offset: "1"
614 , stopColor: "#86b6fe"
615 }
616 ]
617 }
618 , SVG.linearGradient
619 { id: "_Linear9"
620 , x1: "0"
621 , y1: "0"
622 , x2: "1"
623 , y2: "0"
624 , gradientUnits: "userSpaceOnUse"
625 , gradientTransform: "matrix(314.21 0 0 9.97478 340.83 431.758)"
626 , children:
627 [ SVG.stop
628 { offset: "0"
629 , stopColor: "#a9abc1"
630 }
631 , SVG.stop
632 { offset: ".11"
633 , stopColor: "#65687d"
634 }
635 , SVG.stop
636 { offset: ".5"
637 , stopColor: "#c3c5d2"
638 }
639 , SVG.stop
640 { offset: ".89"
641 , stopColor: "#64677c"
642 }
643 , SVG.stop
644 { offset: "1"
645 , stopColor: "#a4a7c2"
646 }
647 ]
648 }
649 , SVG.radialGradient
650 { id: "_Radial3"
651 , cx: "0"
652 , cy: "0"
653 , r: "1"
654 , gradientUnits: "userSpaceOnUse"
655 , gradientTransform: "matrix(647.316 0 0 309.935 720.558 355.142)"
656 , children:
657 [ SVG.stop
658 { offset: "0"
659 , stopColor: "#cfcfcf"
660 , stopOpacity: ".65"
661 }
662 , SVG.stop
663 { offset: "1"
664 , stopColor: "#fff552"
665 , stopOpacity: "0"
666 }
667 ]
668 }
669 ]
670 }
672darkDefs ∷ JSX
673darkDefs =
674 SVG.defs
675 { children:
676 [ SVG.linearGradient
677 { id: "_Linear2"
678 , x1: "0"
679 , y1: "0"
680 , x2: "1"
681 , y2: "0"
682 , gradientUnits: "userSpaceOnUse"
683 , gradientTransform: "matrix(0 451.564 -803.856 0 398.9 -3.513)"
684 , children:
685 [ SVG.stop
686 { offset: "0"
687 , stopColor: "#000434"
688 }
689 , SVG.stop
690 { offset: ".28"
691 , stopColor: "#171043"
692 }
693 , SVG.stop
694 { offset: ".68"
695 , stopColor: "#573774"
696 }
697 , SVG.stop
698 { offset: "1"
699 , stopColor: "#b8734e"
700 }
701 ]
702 }
703 , SVG.linearGradient
704 { id: "_Linear4"
705 , x1: "0"
706 , y1: "0"
707 , x2: "1"
708 , y2: "0"
709 , gradientUnits: "userSpaceOnUse"
710 , gradientTransform: "matrix(-.53832 -17.3125 1.44013 -.04478 749.812 228.864)"
711 , children:
712 [ SVG.stop
713 { offset: "0"
714 , stopColor: "#e59f77"
715 }
716 , SVG.stop
717 { offset: ".47"
718 , stopColor: "#a46075"
719 }
720 , SVG.stop
721 { offset: "1"
722 , stopColor: "#914d74"
723 }
724 ]
725 }
726 , SVG.linearGradient
727 { id: "_Linear5"
728 , x1: "0"
729 , y1: "0"
730 , x2: "1"
731 , y2: "0"
732 , gradientUnits: "userSpaceOnUse"
733 , gradientTransform: "scale(293.8304) rotate(83.86 -.11 .484)"
734 , children:
735 [ SVG.stop
736 { offset: "0"
737 , stopColor: "#08001b"
738 }
739 , SVG.stop
740 { offset: "1"
741 , stopColor: "#571547"
742 }
743 ]
744 }
745 , SVG.linearGradient
746 { id: "_Linear6"
747 , x1: "0"
748 , y1: "0"
749 , x2: "1"
750 , y2: "0"
751 , gradientUnits: "userSpaceOnUse"
752 , gradientTransform: "scale(219.3256) rotate(84.988 -.028 1.133)"
753 , children:
754 [ SVG.stop
755 { offset: "0"
756 , stopColor: "#000418"
757 }
758 , SVG.stop
759 { offset: "1"
760 , stopColor: "#571548"
761 }
762 ]
763 }
764 , SVG.linearGradient
765 { id: "_Linear7"
766 , x1: "0"
767 , y1: "0"
768 , x2: "1"
769 , y2: "0"
770 , gradientUnits: "userSpaceOnUse"
771 , gradientTransform: "matrix(46.7869 123.585 -71.5547 27.0893 259.972 327.713)"
772 , children:
773 [ SVG.stop
774 { offset: "0"
775 , stopColor: "#000418"
776 }
777 , SVG.stop
778 { offset: "1"
779 , stopColor: "#571548"
780 }
781 ]
782 }
783 , SVG.linearGradient
784 { id: "_Linear8"
785 , x1: "0"
786 , y1: "0"
787 , x2: "1"
788 , y2: "0"
789 , gradientUnits: "userSpaceOnUse"
790 , gradientTransform: "rotate(90 -6.304 321.409) scale(123.585)"
791 , children:
792 [ SVG.stop
793 { offset: "0"
794 , stopColor: "#3a0843"
795 }
796 , SVG.stop
797 { offset: "1"
798 , stopColor: "#9f5c6a"
799 }
800 ]
801 }
802 , SVG.linearGradient
803 { id: "_Linear9"
804 , x1: "0"
805 , y1: "0"
806 , x2: "1"
807 , y2: "0"
808 , gradientUnits: "userSpaceOnUse"
809 , gradientTransform: "scale(101.643) rotate(28.107 -6.187 4.945)"
810 , children:
811 [ SVG.stop
812 { offset: "0"
813 , stopColor: "#2c1d2f"
814 }
815 , SVG.stop
816 { offset: "1"
817 , stopColor: "#480445"
818 }
819 ]
820 }
821 , SVG.linearGradient
822 { id: "_Linear10"
823 , x1: "0"
824 , y1: "0"
825 , x2: "1"
826 , y2: "0"
827 , gradientUnits: "userSpaceOnUse"
828 , gradientTransform: "scale(-106.68957) rotate(-86.136 -1.645 -.807)"
829 , children:
830 [ SVG.stop
831 { offset: "0"
832 , stopColor: "#2c1d2f"
833 }
834 , SVG.stop
835 { offset: "1"
836 , stopColor: "#480445"
837 }
838 ]
839 }
840 , SVG.linearGradient
841 { id: "_Linear11"
842 , x1: "0"
843 , y1: "0"
844 , x2: "1"
845 , y2: "0"
846 , gradientUnits: "userSpaceOnUse"
847 , gradientTransform: "matrix(20.5602 0 -184.503 0 11.25 272.442)"
848 , children:
849 [ SVG.stop
850 { offset: "0"
851 , stopColor: "#000624"
852 }
853 , SVG.stop
854 { offset: "1"
855 , stopColor: "#4f1557"
856 }
857 ]
858 }
859 , SVG.linearGradient
860 { id: "_Linear12"
861 , x1: "0"
862 , y1: "0"
863 , x2: "1"
864 , y2: "0"
865 , gradientUnits: "userSpaceOnUse"
866 , gradientTransform: "matrix(314.21 0 0 9.97478 340.83 431.758)"
867 , children:
868 [ SVG.stop
869 { offset: "0"
870 , stopColor: "#a9abc1"
871 }
872 , SVG.stop
873 { offset: ".11"
874 , stopColor: "#65687d"
875 }
876 , SVG.stop
877 { offset: ".5"
878 , stopColor: "#c3c5d2"
879 }
880 , SVG.stop
881 { offset: ".89"
882 , stopColor: "#64677c"
883 }
884 , SVG.stop
885 { offset: "1"
886 , stopColor: "#a4a7c2"
887 }
888 ]
889 }
890 , SVG.radialGradient
891 { id: "_Radial3"
892 , cx: "0"
893 , cy: "0"
894 , r: "1"
895 , gradientUnits: "userSpaceOnUse"
896 , gradientTransform: "matrix(330.479 0 0 252.189 174.599 310.24)"
897 , children:
898 [ SVG.stop
899 { offset: "0"
900 , stopColor: "#f60"
901 , stopOpacity: ".3"
902 }
903 , SVG.stop
904 { offset: "1"
905 , stopColor: "#fff552"
906 , stopOpacity: "0"
907 }
908 ]
909 }
910 ]
911 }
913infixr 8 zip as <%>
915stars ∷ ∀ css. String -> { twinklingStar1 ∷ String, twinklingStar2 ∷ String, twinklingStar3 ∷ String | css } -> JSX
916stars className classes =
917 let
918 n = 400
920 xs = randomRs 0.0 800.0 n $ mkSeed 84
922 ys = randomRs (-20.0) 200.0 n $ mkSeed 4398
924 radiuses = randomRs 0.1 1.3 n (mkSeed 15)
926 opacities = randomRs 0.5 0.9 n (mkSeed 92)
928 classNames =
929 randomRs 1 50 n (mkSeed 3)
930 <#> case _ of
931 1 -> classes.twinklingStar1
932 2 -> classes.twinklingStar2
933 3 -> classes.twinklingStar2
934 _ -> ""
936 zipped = xs <%> ys <%> radiuses <%> classNames <%> opacities
938 children = do
939 cx /\ cy /\ r /\ className' /\ opacity <- zipped
940 pure
941 $ SVG.circle
942 { cx: show cx
943 , cy: show cy
944 , r: show r
945 , fill: "#eeefff"
946 , className: className'
947 , opacity: show (opacity - cy / 200.0)
948 }
949 in
950 SVG.g
951 { fillOpacity: ".67"
952 , children
953 , className
954 }