-
Notifications
You must be signed in to change notification settings - Fork 0
/
View.elm
140 lines (123 loc) · 4.21 KB
/
View.elm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
module View exposing (view)
import Browser exposing (Document)
import Clip exposing (Clip, Word, maxWidth, minSpace)
import Debug
import Html exposing (Html, div, node)
import Html.Attributes exposing (attribute, autoplay, preload, property, src, style, type_)
import Html.Events exposing (on, stopPropagationOn)
import Json.Decode as Decode
import Json.Encode as Encode
import Message exposing (Msg(..))
import Model exposing (ClipState(..), Model, SoundState(..))
import String
import Svg exposing (g, mask, rect, svg, text, text_, tspan)
import Svg.Attributes exposing (dx, dy, fill, height, id, viewBox, width, x, y)
toPx : Int -> String
toPx px =
String.fromInt px ++ "px"
view : Model -> Document Msg
view ({ clip, width, height, count, sound } as m) =
case clip of
Loaded clip_ ->
{ title = clip_.title
, body = [ renderClip sound count m.width m.height clip_ ]
}
_ ->
{ title = "", body = [] }
renderLine : Int -> Int -> List Word -> Html Msg
renderLine size lineNumber line =
let
wordsWidth =
List.foldr (.width >> (+)) 0 line
spaceSize =
if lineNumber < size then
(maxWidth - wordsWidth) // (List.length line - 1)
else
-- don't stretch the spaces on the last line
minSpace
in
text_
[ y (String.fromFloat (0.75 * toFloat (lineNumber + 1)) ++ "em")
, x "0"
]
(List.indexedMap (renderWord spaceSize) line)
renderWord : Int -> Int -> Word -> Html Msg
renderWord spaceSize wordNumber w =
if wordNumber == 0 then
tspan [] [ text w.text ]
else
tspan [ dx (toPx spaceSize) ] [ text w.text ]
renderClip : SoundState -> Int -> Int -> Int -> Clip -> Html Msg
renderClip sound count width_ height_ { video, cover, lines, line, word, caption } =
let
size =
min (min width_ height_ - 50) 800
in
div
[ style "position" "absolute"
, style "left" (toPx ((width_ - size) // 2))
, style "top" (toPx ((height_ - size) // 2))
, style "width" (toPx size)
, style "height" (toPx size)
, style "font" Clip.font
, style "cursor" "pointer"
, stopPropagationOn "click"
(Decode.succeed <|
case sound of
Hidden ->
( Noop, True )
Shown ->
( EnableSound, True )
Enabled ->
( PlayRandom, True )
)
]
[ Html.video
[ type_ "video/mp4"
, src (video ++ "#" ++ String.fromInt count)
, autoplay True
, attribute "playsinline" "playsinline"
, property "muted" (Encode.bool (sound /= Enabled))
, on "ended" (Decode.succeed PlayRandom)
, on "error" (Decode.succeed PlayRandom)
, on "metadata" (Decode.succeed ShowSoundButton)
, style "position" "absolute"
, style "width" "100%"
, style "height" "100%"
, style "background" "black"
]
[]
, svg
[ viewBox "0 0 640 640"
, style "position" "absolute"
, style "width" "101%"
, style "height" "101%"
]
[ mask
[ id ("mask-" ++ String.fromInt (String.length word.text)) ]
-- changing id of the mask forces redraw
[ rect
[ x "0"
, y "0"
, width "100%"
, height "100%"
, fill "#fff"
]
[]
, g []
(List.indexedMap
(renderLine (List.length lines))
(lines ++ [ line ++ [ word ] ])
)
]
, rect
[ x "0"
, y "0"
, width "100%"
, height "100%"
, fill "#fff"
, attribute "mask" ("url(#mask-" ++ String.fromInt (String.length word.text) ++ ")")
]
[]
]
]