smartphoto 2.1.2 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,6 @@ If you are Japasese, See here [https://www.appleple.com/blog/javascript/smartpho
17
17
 
18
18
  ## Installation
19
19
  - [npm](https://www.npmjs.com/package/smartphoto)
20
- - [standalone](https://raw.githubusercontent.com/appleple/smart-photo/master/js/smartphoto.js)
21
20
 
22
21
  via npm
23
22
  ```shell
@@ -189,6 +188,11 @@ Slide fields:
189
188
  <td>minimum swipe distance (px) to trigger navigation/close</td>
190
189
  <td>100</td>
191
190
  </tr>
191
+ <tr>
192
+ <td>swipeVelocity</td>
193
+ <td>minimum swipe speed (px/ms) that triggers navigation even below swipeOffset (fast flicks)</td>
194
+ <td>0.5</td>
195
+ </tr>
192
196
  <tr>
193
197
  <td>headerHeight</td>
194
198
  <td>height (px) reserved for the header when fitting images</td>
@@ -2,26 +2,32 @@
2
2
  from {
3
3
  opacity: 0;
4
4
  }
5
+
5
6
  to {
6
7
  opacity: 1;
7
8
  }
8
9
  }
10
+
9
11
  @keyframes smartphoto-img-wrap {
10
12
  from {
11
13
  opacity: 0;
12
14
  }
15
+
13
16
  to {
14
17
  opacity: 1;
15
18
  }
16
19
  }
20
+
17
21
  @keyframes smartphoto-inner {
18
22
  from {
19
23
  transform: translate(0, 100px);
20
24
  }
25
+
21
26
  to {
22
27
  transform: translate(0, 0);
23
28
  }
24
29
  }
30
+
25
31
  @keyframes smartphoto-loader {
26
32
  0% {
27
33
  opacity: 0.4;
@@ -36,14 +42,23 @@
36
42
  transform: rotate(360deg);
37
43
  }
38
44
  }
45
+
46
+ /* display の切り替えをキーフレーム内でアニメーションさせていた版は、閉じている間
47
+ * (dialog が display:none)にアニメーションのタイムラインが進まなくなり、再度開いた
48
+ * 際に 0%/1% の display:none で止まったままになる実機バグがあった(矢印/ナビが
49
+ * クリック不能になり、その下の背景をタップしたと判定されてモーダルが閉じてしまう)。
50
+ * 表示/非表示そのものは常に aria-hidden 属性のセレクタ側(下記)で確定させ、
51
+ * ここでは opacity のフェードのみをアニメーションする */
39
52
  @keyframes smartphoto-appear {
40
53
  0% {
41
54
  opacity: 0;
42
55
  }
56
+
43
57
  100% {
44
58
  opacity: 1;
45
59
  }
46
60
  }
61
+
47
62
  @keyframes smartphoto-hide {
48
63
  0% {
49
64
  opacity: 1;
@@ -52,36 +67,72 @@
52
67
  opacity: 0;
53
68
  }
54
69
  }
70
+
55
71
  :root:has(dialog.smartphoto[open]) {
56
72
  overflow: hidden;
57
73
  }
58
74
 
75
+ /* dialogタグの表示制御 */
59
76
  .smartphoto {
77
+ /* dialogタグのデフォルトスタイルをリセット */
60
78
  border: none;
61
79
  background: transparent;
62
80
  padding: 0;
63
81
  margin: 0;
64
82
  outline: none;
83
+ /* 既存のスタイルを適用 */
65
84
  position: fixed;
66
85
  inset: 0;
67
86
  width: 100%;
68
87
  height: 100%;
69
88
  max-width: 100%;
70
89
  max-height: 100%;
90
+ /* dialog 自体には overflow の既定値が無く、子要素(nav/arrows 等)が僅かに
91
+ * ボックスを超えるだけで dialog 自身がスクロール可能になってしまう。
92
+ * 内側のスクロールは常に禁止し、外側の背景スクロール抑制(:root:has())と
93
+ * 合わせて完全に固定表示にする */
71
94
  overflow: hidden;
72
- height: 100dvh;
73
- max-height: 100dvh;
74
- background-color: var(--smartphoto-backdrop-color, rgb(0, 0, 0));
95
+ /* モバイルでスクロールに伴いアドレスバーが表示/非表示になり可視領域が変化しても
96
+ * 追従するよう、dvh (動的ビューポート高さ) を優先する
97
+ * (see: https://github.com/appleple/SmartPhoto/issues/90)
98
+ * dvh はブラウザによってはアドレスバーの伸縮への追従が実装依存で不安定なため、
99
+ * JS で実測して設定する --smartphoto-vh (updateViewportHeight() 参照) があれば
100
+ * それを優先し、JS 無効時やブラウザ未対応時は height:100%/max-height:100% に
101
+ * フォールバックする */
102
+ height: var(--smartphoto-vh, 100dvh);
103
+ max-height: var(--smartphoto-vh, 100dvh);
104
+ background-color: var(--smartphoto-backdrop-color, rgba(0, 0, 0, 1));
75
105
  opacity: 1;
76
106
  font-family: sans-serif;
77
107
  cursor: pointer;
78
- transition: opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out), display var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out) allow-discrete, overlay var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out) allow-discrete;
108
+ /* close() [open] 属性を外すと同時にネイティブに display:none / トップレイヤー
109
+ * からの除外を行う(§8)。display・overlay を allow-discrete で transition
110
+ * 対象に加えることで、opacity のフェードが完了するまでその適用を1フレーム分
111
+ * 遅らせ、閉じる瞬間に「ぱっと」消えず滑らかにフェードアウトするようにする */
112
+ transition:
113
+ opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out),
114
+ display var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out) allow-discrete,
115
+ overlay var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out) allow-discrete;
79
116
  }
117
+
80
118
  .smartphoto:not([open]) {
81
119
  opacity: 0;
82
120
  }
121
+
83
122
  .smartphoto::backdrop {
84
- background-color: var(--smartphoto-backdrop-color, rgb(0, 0, 0));
123
+ background-color: var(--smartphoto-backdrop-color, rgba(0, 0, 0, 1));
124
+ }
125
+
126
+ /* View Transition(openPhotoWithViewTransition() 参照)の疑似要素ツリーは document の
127
+ * ルート要素(html)の子として扱われ、dialog に設定した --smartphoto-animation-speed を
128
+ * 継承しない。そのため JS 側では document.documentElement にも同じ変数を設定しており、
129
+ * ここではその値を参照する。指定がなければブラウザ既定(実装依存, 概ね0.25s前後で
130
+ * 速すぎるとの声があった)の代わりに 0.3s を使う */
131
+ ::view-transition-group(smartphoto-hero),
132
+ ::view-transition-old(smartphoto-hero),
133
+ ::view-transition-new(smartphoto-hero) {
134
+ animation-duration: var(--smartphoto-animation-speed, 0.3s);
135
+ animation-timing-function: var(--smartphoto-animation-function, ease-out);
85
136
  }
86
137
 
87
138
  .smartphoto-close {
@@ -114,6 +165,7 @@
114
165
  left: 0;
115
166
  width: 100%;
116
167
  height: 100%;
168
+ /* Pointer Events(swipe/pinch)がブラウザ既定のスクロール・ズームと衝突しないようにする */
117
169
  touch-action: none;
118
170
  }
119
171
 
@@ -162,6 +214,7 @@
162
214
  -ms-user-select: none;
163
215
  user-select: none;
164
216
  transition: transform var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);
217
+
165
218
  -webkit-user-drag: none;
166
219
  }
167
220
 
@@ -182,6 +235,7 @@
182
235
  .smartphoto-img-wrap {
183
236
  display: inline-block;
184
237
  opacity: 1;
238
+ /* Pointer Events(ピンチ/ドラッグ)がブラウザ既定のスクロール・ズームと衝突しないようにする */
185
239
  touch-action: none;
186
240
  -webkit-transition: opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);
187
241
  -moz-transition: opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);
@@ -215,7 +269,7 @@
215
269
  animation-timing-function: var(--smartphoto-animation-function, ease-out);
216
270
  }
217
271
 
218
- .smartphoto-arrows[aria-hidden=true] {
272
+ .smartphoto-arrows[aria-hidden='true'] {
219
273
  animation-name: smartphoto-hide;
220
274
  display: none;
221
275
  }
@@ -232,16 +286,19 @@
232
286
  animation-timing-function: var(--smartphoto-animation-function, ease-out);
233
287
  animation-name: smartphoto-appear;
234
288
  }
289
+
235
290
  .smartphoto-arrows li:focus {
236
291
  outline: none;
237
292
  }
238
293
 
239
- .smartphoto-arrows [aria-hidden=true] {
294
+ .smartphoto-arrows [aria-hidden='true'] {
240
295
  animation-name: smartphoto-hide;
241
296
  display: none;
242
297
  }
243
298
 
244
299
  .smartphoto-arrows button {
300
+ /* <button> の UA デフォルト(border/padding/背景/appearance)をリセットし、
301
+ * 旧 <a> と同じ「全面クリック領域 + 背景 SVG アイコン」の見た目を維持する */
245
302
  appearance: none;
246
303
  display: block;
247
304
  width: 100%;
@@ -258,6 +315,7 @@
258
315
  padding: 5px 0;
259
316
  background-color: rgba(0, 0, 0, 0.5);
260
317
  }
318
+
261
319
  .smartphoto-arrow-right button {
262
320
  background-image: url(data:image/svg+xml;base64,PHN2ZyBpZD0i44Os44Kk44Ok44O8XzEiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDI4MzQuNjUgMjgzNC42NSIgZmlsbD0iI0ZGRiAiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNMTgzNy44OCwxNDE3LjMyLDY0My41OSwyMjNhNzIuMjEsNzIuMjEsMCwwLDEsMC0xMDEuODJMNzQzLjgyLDIxYTcyLjIxLDcyLjIxLDAsMCwxLDEwMS44MiwwTDIwOTAuODMsMTI2Ni4xOWwxMDAuMjMsMTAwLjIzYTcyLjIxLDcyLjIxLDAsMCwxLDAsMTAxLjgyTDg0NS42NCwyODEzLjY1YTcyLjIxLDcyLjIxLDAsMCwxLTEwMS44MiwwTDY0My41OSwyNzEzLjQyYTcyLjIxLDcyLjIxLDAsMCwxLDAtMTAxLjgyWiIvPjwvc3ZnPg==);
263
321
  }
@@ -267,6 +325,7 @@
267
325
  padding: 5px 0;
268
326
  background-color: rgba(0, 0, 0, 0.5);
269
327
  }
328
+
270
329
  .smartphoto-arrow-left button {
271
330
  background-image: url(data:image/svg+xml;base64,PHN2ZyBpZD0i44Os44Kk44Ok44O8XzEiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDI4MzQuNjUgMjgzNC42NSIgZmlsbD0iI0ZGRiI+PHRpdGxlPmljb248L3RpdGxlPjxwYXRoIGQ9Ik05OTYuNzcsMTQxNy4zMiwyMTkxLjA2LDIyM2E3Mi4yMSw3Mi4yMSwwLDAsMCwwLTEwMS44MkwyMDkwLjgzLDIxQTcyLjIxLDcyLjIxLDAsMCwwLDE5ODksMjFMNzQzLjgyLDEyNjYuMTksNjQzLjU5LDEzNjYuNDJhNzIuMjEsNzIuMjEsMCwwLDAsMCwxMDEuODJMMTk4OSwyODEzLjY1YTcyLjIxLDcyLjIxLDAsMCwwLDEwMS44MiwwbDEwMC4yMy0xMDAuMjNhNzIuMjEsNzIuMjEsMCwwLDAsMC0xMDEuODJaIi8+PC9zdmc+);
272
331
  }
@@ -286,7 +345,7 @@
286
345
  animation-timing-function: var(--smartphoto-animation-function, ease-out);
287
346
  }
288
347
 
289
- .smartphoto-nav[aria-hidden=true] {
348
+ .smartphoto-nav[aria-hidden='true'] {
290
349
  animation-name: smartphoto-hide;
291
350
  display: none;
292
351
  }
@@ -299,6 +358,7 @@
299
358
  padding: 0;
300
359
  text-align: center;
301
360
  white-space: nowrap;
361
+
302
362
  -webkit-overflow-scrolling: touch;
303
363
  }
304
364
 
@@ -310,6 +370,8 @@
310
370
  }
311
371
 
312
372
  .smartphoto-nav button {
373
+ /* <button> の UA デフォルト(border/padding/appearance)をリセットし、
374
+ * 旧 <a> と同じサムネイル背景画像ボタンの見た目を維持する */
313
375
  appearance: none;
314
376
  display: block;
315
377
  width: 100%;
@@ -323,6 +385,7 @@
323
385
  opacity: 0.5;
324
386
  cursor: pointer;
325
387
  }
388
+
326
389
  .smartphoto-nav button:focus {
327
390
  opacity: 0.8;
328
391
  }
@@ -346,6 +409,7 @@
346
409
  padding: 0;
347
410
  white-space: nowrap;
348
411
  }
412
+
349
413
  .smartphoto-list li {
350
414
  display: block;
351
415
  position: absolute;
@@ -355,6 +419,7 @@
355
419
  height: 100%;
356
420
  transition: all var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);
357
421
  }
422
+
358
423
  .smartphoto-list li:focus {
359
424
  outline: none;
360
425
  }
@@ -381,6 +446,7 @@
381
446
  margin: 0;
382
447
  font-weight: normal;
383
448
  }
449
+
384
450
  .smartphoto-caption:focus {
385
451
  outline: none;
386
452
  }
@@ -392,7 +458,11 @@
392
458
  width: 0;
393
459
  height: 0;
394
460
  transform: translate(50vw, 50vh);
461
+ /* dvh 対応ブラウザでは動的ビューポート高さを基準に中央寄せする
462
+ * (see: https://github.com/appleple/SmartPhoto/issues/90)。
463
+ * --smartphoto-vh (updateViewportHeight() 参照) が設定されていればそちらを優先する(§) */
395
464
  transform: translate(50vw, 50dvh);
465
+ transform: translate(50vw, calc(var(--smartphoto-vh, 100dvh) / 2));
396
466
  }
397
467
 
398
468
  .smartphoto-loader {
@@ -428,5 +498,3 @@
428
498
  border: 0;
429
499
  clip: rect(0, 0, 0, 0);
430
500
  }
431
-
432
- /*# sourceMappingURL=smartphoto.css.map */
@@ -1 +1 @@
1
- @keyframes smartphoto{from{opacity:0}to{opacity:1}}@keyframes smartphoto-img-wrap{from{opacity:0}to{opacity:1}}@keyframes smartphoto-inner{from{transform:translate(0, 100px)}to{transform:translate(0, 0)}}@keyframes smartphoto-loader{0%{opacity:.4;transform:rotate(0deg)}50%{opacity:1;transform:rotate(180deg)}100%{opacity:.4;transform:rotate(360deg)}}@keyframes smartphoto-appear{0%{opacity:0}100%{opacity:1}}@keyframes smartphoto-hide{0%{opacity:1}100%{opacity:0}}:root:has(dialog.smartphoto[open]){overflow:hidden}.smartphoto{border:none;background:rgba(0,0,0,0);padding:0;margin:0;outline:none;position:fixed;inset:0;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden;height:100dvh;max-height:100dvh;background-color:var(--smartphoto-backdrop-color, rgb(0, 0, 0));opacity:1;font-family:sans-serif;cursor:pointer;transition:opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out),display var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out) allow-discrete,overlay var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out) allow-discrete}.smartphoto:not([open]){opacity:0}.smartphoto::backdrop{background-color:var(--smartphoto-backdrop-color, rgb(0, 0, 0))}.smartphoto-close{opacity:0}.smartphoto-count{display:inline-block;color:#fff;font-size:16px}.smartphoto-header{display:block;box-sizing:border-box;position:fixed;z-index:102;top:0;left:0;width:100%;height:50px;padding:15px;background-color:var(--smartphoto-header-color, rgba(0, 0, 0, 0.2))}.smartphoto-content{display:block;position:absolute;top:0;left:0;width:100%;height:100%;touch-action:none}.smartphoto-dismiss{display:block;position:absolute;top:15px;right:10px;width:20px;height:20px;padding:0;border:none;background-color:rgba(0,0,0,0);background-image:url(data:image/svg+xml;base64,PHN2ZyBpZD0i44Os44Kk44Ok44O8XzEiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDI4MzQuNjUgMjgzNC42NSIgZmlsbD0iI0ZGRiI+PHRpdGxlPmljb248L3RpdGxlPjxwYXRoIGQ9Ik0xNTc2LjQyLDE0MDYuNzYsMjc4NCwxOTkuMTlhNTYuODYsNTYuODYsMCwwLDAsMC04MC4xOGwtNzguOTItNzguOTJhNTYuODYsNTYuODYsMCwwLDAtODAuMTgsMEwxNDE3LjMyLDEyNDcuNjYsMjA5Ljc1LDQwLjA5YTU2Ljg2LDU2Ljg2LDAsMCwwLTgwLjE4LDBMNTAuNjUsMTE5YTU2Ljg2LDU2Ljg2LDAsMCwwLDAsODAuMThMMTI1OC4yMywxNDA2Ljc2LDUwLjY1LDI2MTQuMzRhNTYuODYsNTYuODYsMCwwLDAsMCw4MC4xOGw3OC45Miw3OC45MmE1Ni44Niw1Ni44NiwwLDAsMCw4MC4xOCwwTDE0MTcuMzIsMTU2NS44NiwyNjI0LjksMjc3My40NGE1Ni44Niw1Ni44NiwwLDAsMCw4MC4xOCwwbDc4LjkyLTc4LjkyYTU2Ljg2LDU2Ljg2LDAsMCwwLDAtODAuMThaIi8+PC9zdmc+);text-shadow:0 1px 0 #fff;color:#fff;font-size:30px;text-decoration:none;cursor:pointer;line-height:1}.smartphoto-body{position:relative;z-index:102;width:100%;height:100%;margin:0 auto}.smartphoto-inner{position:relative;width:100%;height:100%;vertical-align:top}.smartphoto-img{display:none;max-width:none;width:auto;height:auto;cursor:zoom-in;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;transition:transform var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);-webkit-user-drag:none}.smartphoto-img.active{display:block}.smartphoto-img-onmove{cursor:grab;cursor:-webkit-grab;transition:none}.smartphoto-img-elasticmove{transition:transform var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out)}.smartphoto-img-wrap{display:inline-block;opacity:1;touch-action:none;-webkit-transition:opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);-moz-transition:opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);-ms-transition:opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);-o-transition:opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);transition:opacity var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out);animation-name:smartphoto-img-wrap;animation-duration:var(--smartphoto-animation-speed, 0.3s);animation-timing-function:var(--smartphoto-animation-function, ease-out)}.smartphoto-img-left{transform:translateX(150%) !important}.smartphoto-img-right{transform:translateX(-150%) !important}.smartphoto-arrows{list-style-type:none;margin:0;padding:0;position:relative;z-index:1002;top:50%;left:0;opacity:1;animation-name:smartphoto-appear;animation-duration:var(--smartphoto-animation-speed, 0.3s);animation-timing-function:var(--smartphoto-animation-function, ease-out)}.smartphoto-arrows[aria-hidden=true]{animation-name:smartphoto-hide;display:none}.smartphoto-arrows li{display:block;position:absolute;top:50%;width:30px;height:30px;margin-top:-20px;box-sizing:content-box;animation-duration:var(--smartphoto-animation-speed, 0.3s);animation-timing-function:var(--smartphoto-animation-function, ease-out);animation-name:smartphoto-appear}.smartphoto-arrows li:focus{outline:none}.smartphoto-arrows [aria-hidden=true]{animation-name:smartphoto-hide;display:none}.smartphoto-arrows button{appearance:none;display:block;width:100%;height:100%;margin:0;padding:0;border:none;background-color:rgba(0,0,0,0);cursor:pointer}.smartphoto-arrow-right{right:0;padding:5px 0;background-color:rgba(0,0,0,.5)}.smartphoto-arrow-right button{background-image:url(data:image/svg+xml;base64,PHN2ZyBpZD0i44Os44Kk44Ok44O8XzEiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDI4MzQuNjUgMjgzNC42NSIgZmlsbD0iI0ZGRiAiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNMTgzNy44OCwxNDE3LjMyLDY0My41OSwyMjNhNzIuMjEsNzIuMjEsMCwwLDEsMC0xMDEuODJMNzQzLjgyLDIxYTcyLjIxLDcyLjIxLDAsMCwxLDEwMS44MiwwTDIwOTAuODMsMTI2Ni4xOWwxMDAuMjMsMTAwLjIzYTcyLjIxLDcyLjIxLDAsMCwxLDAsMTAxLjgyTDg0NS42NCwyODEzLjY1YTcyLjIxLDcyLjIxLDAsMCwxLTEwMS44MiwwTDY0My41OSwyNzEzLjQyYTcyLjIxLDcyLjIxLDAsMCwxLDAtMTAxLjgyWiIvPjwvc3ZnPg==)}.smartphoto-arrow-left{left:0;padding:5px 0;background-color:rgba(0,0,0,.5)}.smartphoto-arrow-left button{background-image:url(data:image/svg+xml;base64,PHN2ZyBpZD0i44Os44Kk44Ok44O8XzEiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDI4MzQuNjUgMjgzNC42NSIgZmlsbD0iI0ZGRiI+PHRpdGxlPmljb248L3RpdGxlPjxwYXRoIGQ9Ik05OTYuNzcsMTQxNy4zMiwyMTkxLjA2LDIyM2E3Mi4yMSw3Mi4yMSwwLDAsMCwwLTEwMS44MkwyMDkwLjgzLDIxQTcyLjIxLDcyLjIxLDAsMCwwLDE5ODksMjFMNzQzLjgyLDEyNjYuMTksNjQzLjU5LDEzNjYuNDJhNzIuMjEsNzIuMjEsMCwwLDAsMCwxMDEuODJMMTk4OSwyODEzLjY1YTcyLjIxLDcyLjIxLDAsMCwwLDEwMS44MiwwbDEwMC4yMy0xMDAuMjNhNzIuMjEsNzIuMjEsMCwwLDAsMC0xMDEuODJaIi8+PC9zdmc+)}.smartPhotoArrowHideIcon{display:none}.smartphoto-nav{position:absolute;bottom:0;left:0;width:100%;opacity:1;animation-name:smartphoto-appear;animation-duration:var(--smartphoto-animation-speed, 0.3s);animation-timing-function:var(--smartphoto-animation-function, ease-out)}.smartphoto-nav[aria-hidden=true]{animation-name:smartphoto-hide;display:none}.smartphoto-nav ul{display:block;overflow-x:auto;list-style:none;margin:0;padding:0;text-align:center;white-space:nowrap;-webkit-overflow-scrolling:touch}.smartphoto-nav li{display:inline-block;overflow:hidden;width:50px;height:50px}.smartphoto-nav button{appearance:none;display:block;width:100%;height:100%;margin:0;padding:0;border:none;background-color:#fff;background-position:center center;background-size:cover;opacity:.5;cursor:pointer}.smartphoto-nav button:focus{opacity:.8}.smartphoto-nav button.current{opacity:1}.smartphoto-nav img{width:auto;height:100%}.smartphoto-list{list-style-type:none;position:absolute;z-index:101;top:0;left:0;margin:0;padding:0;white-space:nowrap}.smartphoto-list li{display:block;position:absolute;top:0;left:0;width:100%;height:100%;transition:all var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out)}.smartphoto-list li:focus{outline:none}.smartphoto-list-onmove{transition:all .3s var(--smartphoto-animation-function, ease-out)}.smartphoto-caption{overflow:hidden;box-sizing:border-box;position:absolute;top:0;left:0;width:100%;height:50px;padding:0 50px;color:#fff;font-size:12px;text-align:center;line-height:50px;white-space:nowrap;text-overflow:ellipsis;margin:0;font-weight:normal}.smartphoto-caption:focus{outline:none}.smartphoto-loader-wrap{display:block;position:relative;z-index:103;width:0;height:0;transform:translate(50vw, 50vh);transform:translate(50vw, 50dvh)}.smartphoto-loader{position:absolute;z-index:101;top:0;left:0;width:30px;height:30px;margin-top:-25px;margin-left:-25px;border:8px solid #17cddd;border-right-color:rgba(0,0,0,0);border-radius:50%;animation:smartphoto-loader .5s infinite linear}.smartphoto-img-clone{position:fixed;z-index:100;top:0;left:0;transition:all var(--smartphoto-animation-speed, 0.3s) var(--smartphoto-animation-function, ease-out)}.smartphoto-sr-only{overflow:hidden;position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;clip:rect(0, 0, 0, 0)}
1
+ @keyframes smartphoto{0%{opacity:0}to{opacity:1}}@keyframes smartphoto-img-wrap{0%{opacity:0}to{opacity:1}}@keyframes smartphoto-inner{0%{transform:translateY(100px)}to{transform:translate(0)}}@keyframes smartphoto-loader{0%{opacity:.4;transform:rotate(0deg)}50%{opacity:1;transform:rotate(180deg)}to{opacity:.4;transform:rotate(1turn)}}@keyframes smartphoto-appear{0%{opacity:0}to{opacity:1}}@keyframes smartphoto-hide{0%{opacity:1}to{opacity:0}}:root:has(dialog.smartphoto[open]){overflow:hidden}.smartphoto{border:none;background:transparent;padding:0;margin:0;outline:none;position:fixed;inset:0;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden;height:var(--smartphoto-vh,100dvh);max-height:var(--smartphoto-vh,100dvh);background-color:var(--smartphoto-backdrop-color,#000);opacity:1;font-family:sans-serif;cursor:pointer;transition:opacity var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out),display var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out) allow-discrete,overlay var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out) allow-discrete}.smartphoto:not([open]){opacity:0}.smartphoto::backdrop{background-color:var(--smartphoto-backdrop-color,#000)}::view-transition-group(smartphoto-hero),::view-transition-new(smartphoto-hero),::view-transition-old(smartphoto-hero){animation-duration:var(--smartphoto-animation-speed,.3s);animation-timing-function:var(--smartphoto-animation-function,ease-out)}.smartphoto-close{opacity:0}.smartphoto-count{display:inline-block;color:#fff;font-size:16px}.smartphoto-header{display:block;box-sizing:border-box;position:fixed;z-index:102;top:0;left:0;width:100%;height:50px;padding:15px;background-color:var(--smartphoto-header-color,rgba(0,0,0,.2))}.smartphoto-content{display:block;position:absolute;top:0;left:0;width:100%;height:100%;touch-action:none}.smartphoto-dismiss{display:block;position:absolute;top:15px;right:10px;width:20px;height:20px;padding:0;border:none;background-color:transparent;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHZpZXdCb3g9IjAgMCAyODM0LjY1IDI4MzQuNjUiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNMTU3Ni40MiAxNDA2Ljc2IDI3ODQgMTk5LjE5YTU2Ljg2IDU2Ljg2IDAgMCAwIDAtODAuMThsLTc4LjkyLTc4LjkyYTU2Ljg2IDU2Ljg2IDAgMCAwLTgwLjE4IDBMMTQxNy4zMiAxMjQ3LjY2IDIwOS43NSA0MC4wOWE1Ni44NiA1Ni44NiAwIDAgMC04MC4xOCAwTDUwLjY1IDExOWE1Ni44NiA1Ni44NiAwIDAgMCAwIDgwLjE4bDEyMDcuNTggMTIwNy41OEw1MC42NSAyNjE0LjM0YTU2Ljg2IDU2Ljg2IDAgMCAwIDAgODAuMThsNzguOTIgNzguOTJhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGwxMjA3LjU3LTEyMDcuNThMMjYyNC45IDI3NzMuNDRhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGw3OC45Mi03OC45MmE1Ni44NiA1Ni44NiAwIDAgMCAwLTgwLjE4WiIvPjwvc3ZnPg==);text-shadow:0 1px 0 #fff;color:#fff;font-size:30px;text-decoration:none;cursor:pointer;line-height:1}.smartphoto-body{z-index:102;margin:0 auto}.smartphoto-body,.smartphoto-inner{position:relative;width:100%;height:100%}.smartphoto-inner{vertical-align:top}.smartphoto-img{display:none;max-width:none;width:auto;height:auto;cursor:zoom-in;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;transition:transform var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out);-webkit-user-drag:none}.smartphoto-img.active{display:block}.smartphoto-img-onmove{cursor:grab;cursor:-webkit-grab;transition:none}.smartphoto-img-elasticmove{transition:transform var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out)}.smartphoto-img-wrap{display:inline-block;opacity:1;touch-action:none;-webkit-transition:opacity var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out);-moz-transition:opacity var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out);-ms-transition:opacity var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out);-o-transition:opacity var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out);transition:opacity var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out);animation-name:smartphoto-img-wrap;animation-duration:var(--smartphoto-animation-speed,.3s);animation-timing-function:var(--smartphoto-animation-function,ease-out)}.smartphoto-img-left{transform:translateX(150%)!important}.smartphoto-img-right{transform:translateX(-150%)!important}.smartphoto-arrows{list-style-type:none;margin:0;padding:0;position:relative;z-index:1002;top:50%;left:0;opacity:1;animation-name:smartphoto-appear;animation-duration:var(--smartphoto-animation-speed,.3s);animation-timing-function:var(--smartphoto-animation-function,ease-out)}.smartphoto-arrows[aria-hidden=true]{animation-name:smartphoto-hide;display:none}.smartphoto-arrows li{display:block;position:absolute;top:50%;width:30px;height:30px;margin-top:-20px;box-sizing:content-box;animation-duration:var(--smartphoto-animation-speed,.3s);animation-timing-function:var(--smartphoto-animation-function,ease-out);animation-name:smartphoto-appear}.smartphoto-arrows li:focus{outline:none}.smartphoto-arrows [aria-hidden=true]{animation-name:smartphoto-hide;display:none}.smartphoto-arrows button{appearance:none;display:block;width:100%;height:100%;margin:0;padding:0;border:none;background-color:transparent;cursor:pointer}.smartphoto-arrow-right{right:0;padding:5px 0;background-color:rgba(0,0,0,.5)}.smartphoto-arrow-right button{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHZpZXdCb3g9IjAgMCAyODM0LjY1IDI4MzQuNjUiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNMTgzNy44OCAxNDE3LjMyIDY0My41OSAyMjNhNzIuMjEgNzIuMjEgMCAwIDEgMC0xMDEuODJMNzQzLjgyIDIxYTcyLjIxIDcyLjIxIDAgMCAxIDEwMS44MiAwbDEyNDUuMTkgMTI0NS4xOSAxMDAuMjMgMTAwLjIzYTcyLjIxIDcyLjIxIDAgMCAxIDAgMTAxLjgyTDg0NS42NCAyODEzLjY1YTcyLjIxIDcyLjIxIDAgMCAxLTEwMS44MiAwbC0xMDAuMjMtMTAwLjIzYTcyLjIxIDcyLjIxIDAgMCAxIDAtMTAxLjgyWiIvPjwvc3ZnPg==)}.smartphoto-arrow-left{left:0;padding:5px 0;background-color:rgba(0,0,0,.5)}.smartphoto-arrow-left button{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHZpZXdCb3g9IjAgMCAyODM0LjY1IDI4MzQuNjUiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNOTk2Ljc3IDE0MTcuMzIgMjE5MS4wNiAyMjNhNzIuMjEgNzIuMjEgMCAwIDAgMC0xMDEuODJMMjA5MC44MyAyMUE3Mi4yMSA3Mi4yMSAwIDAgMCAxOTg5IDIxTDc0My44MiAxMjY2LjE5bC0xMDAuMjMgMTAwLjIzYTcyLjIxIDcyLjIxIDAgMCAwIDAgMTAxLjgyTDE5ODkgMjgxMy42NWE3Mi4yMSA3Mi4yMSAwIDAgMCAxMDEuODIgMGwxMDAuMjMtMTAwLjIzYTcyLjIxIDcyLjIxIDAgMCAwIDAtMTAxLjgyWiIvPjwvc3ZnPg==)}.smartPhotoArrowHideIcon{display:none}.smartphoto-nav{position:absolute;bottom:0;left:0;width:100%;opacity:1;animation-name:smartphoto-appear;animation-duration:var(--smartphoto-animation-speed,.3s);animation-timing-function:var(--smartphoto-animation-function,ease-out)}.smartphoto-nav[aria-hidden=true]{animation-name:smartphoto-hide;display:none}.smartphoto-nav ul{display:block;overflow-x:auto;list-style:none;margin:0;padding:0;text-align:center;white-space:nowrap;-webkit-overflow-scrolling:touch}.smartphoto-nav li{display:inline-block;overflow:hidden;width:50px;height:50px}.smartphoto-nav button{appearance:none;display:block;width:100%;height:100%;margin:0;padding:0;border:none;background-color:#fff;background-position:50%;background-size:cover;opacity:.5;cursor:pointer}.smartphoto-nav button:focus{opacity:.8}.smartphoto-nav button.current{opacity:1}.smartphoto-nav img{width:auto;height:100%}.smartphoto-list{list-style-type:none;position:absolute;z-index:101;top:0;left:0;margin:0;padding:0;white-space:nowrap}.smartphoto-list li{display:block;position:absolute;top:0;left:0;width:100%;height:100%;transition:all var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out)}.smartphoto-list li:focus{outline:none}.smartphoto-list-onmove{transition:all .3s var(--smartphoto-animation-function,ease-out)}.smartphoto-caption{overflow:hidden;box-sizing:border-box;position:absolute;top:0;left:0;width:100%;height:50px;padding:0 50px;color:#fff;font-size:12px;text-align:center;line-height:50px;white-space:nowrap;text-overflow:ellipsis;margin:0;font-weight:400}.smartphoto-caption:focus{outline:none}.smartphoto-loader-wrap{display:block;position:relative;z-index:103;width:0;height:0;transform:translate(50vw,50vh);transform:translate(50vw,50dvh);transform:translate(50vw,calc(var(--smartphoto-vh, 100dvh)/2))}.smartphoto-loader{position:absolute;z-index:101;top:0;left:0;width:30px;height:30px;margin-top:-25px;margin-left:-25px;border:8px solid #17cddd;border-right-color:transparent;border-radius:50%;animation:smartphoto-loader .5s linear infinite}.smartphoto-img-clone{position:fixed;z-index:100;top:0;left:0;transition:all var(--smartphoto-animation-speed,.3s) var(--smartphoto-animation-function,ease-out)}.smartphoto-sr-only{overflow:hidden;position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;clip:rect(0,0,0,0)}
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * SmartPhoto v2.1.2
2
+ * SmartPhoto v2.1.4
3
3
  * (c) appleple
4
4
  * Released under the MIT License.
5
5
  */
@@ -106,6 +106,7 @@
106
106
  swipeTopToClose: false,
107
107
  swipeBottomToClose: true,
108
108
  swipeOffset: 100,
109
+ swipeVelocity: 0.5,
109
110
  headerHeight: 60,
110
111
  footerHeight: 60,
111
112
  forceInterval: 10,
@@ -366,6 +367,7 @@
366
367
  const y = p1.y - p2.y;
367
368
  return Math.sqrt(x * x + y * y);
368
369
  }
370
+ var MIN_FLICK_DISTANCE = 10;
369
371
  function getForceAndTheta(x, y) {
370
372
  return { force: Math.sqrt(x * x + y * y), theta: Math.atan2(y, x) };
371
373
  }
@@ -383,6 +385,7 @@
383
385
  let firstPos = null;
384
386
  let oldPos = null;
385
387
  let moveDir = null;
388
+ let swipeStartTime = 0;
386
389
  let photoSwipable = false;
387
390
  let firstPhotoPos = null;
388
391
  let oldPhotoPos = null;
@@ -390,6 +393,7 @@
390
393
  let photoVY = 0;
391
394
  let pinching = false;
392
395
  let oldDistance = 0;
396
+ let pinchMoveFrame = null;
393
397
  let vx = 0;
394
398
  let vy = 0;
395
399
  function isSmartPhone2() {
@@ -511,6 +515,7 @@
511
515
  dragStart = true;
512
516
  firstPos = pos;
513
517
  oldPos = pos;
518
+ swipeStartTime = Date.now();
514
519
  }
515
520
  function startPhotoDrag(e) {
516
521
  photoSwipable = true;
@@ -535,6 +540,23 @@
535
540
  }
536
541
  startSwipe(e);
537
542
  }
543
+ function scheduleGestureMove() {
544
+ if (pinchMoveFrame !== null) {
545
+ return;
546
+ }
547
+ pinchMoveFrame = requestAnimationFrame(() => {
548
+ pinchMoveFrame = null;
549
+ callbacks.onGestureMove();
550
+ });
551
+ }
552
+ function flushGestureMove() {
553
+ if (pinchMoveFrame === null) {
554
+ return;
555
+ }
556
+ cancelAnimationFrame(pinchMoveFrame);
557
+ pinchMoveFrame = null;
558
+ callbacks.onGestureMove();
559
+ }
538
560
  function movePinch() {
539
561
  const points = Array.from(activePointers.values());
540
562
  const dist = distance(
@@ -559,7 +581,7 @@
559
581
  state.viewer.hideUi = state.viewer.scaleSize < 1 || state.viewer.scaleSize > border;
560
582
  }
561
583
  oldDistance = dist;
562
- callbacks.onGestureMove();
584
+ scheduleGestureMove();
563
585
  }
564
586
  function moveSwipe(e) {
565
587
  const pos = getPos(e);
@@ -608,6 +630,7 @@
608
630
  }
609
631
  function endPinch() {
610
632
  pinching = false;
633
+ flushGestureMove();
611
634
  const item = currentItem(state);
612
635
  if (!item) {
613
636
  return;
@@ -645,9 +668,11 @@
645
668
  const items = (_a = currentItems(state)) != null ? _a : [];
646
669
  if (moveDir === "horizontal") {
647
670
  let result = "stay";
648
- if (swipeWidth >= state.options.swipeOffset && state.viewer.currentIndex !== 0) {
671
+ const elapsedMs = Math.max(now - swipeStartTime, 1);
672
+ const isFlick = Math.abs(swipeWidth) >= MIN_FLICK_DISTANCE && Math.abs(swipeWidth) / elapsedMs >= state.options.swipeVelocity;
673
+ if ((swipeWidth >= state.options.swipeOffset || isFlick && swipeWidth > 0) && state.viewer.currentIndex !== 0) {
649
674
  result = "prev";
650
- } else if (swipeWidth <= -state.options.swipeOffset && state.viewer.currentIndex !== items.length - 1) {
675
+ } else if ((swipeWidth <= -state.options.swipeOffset || isFlick && swipeWidth < 0) && state.viewer.currentIndex !== items.length - 1) {
651
676
  result = "next";
652
677
  }
653
678
  callbacks.onSwipeEnd(result);
@@ -738,6 +763,10 @@
738
763
  }
739
764
  function detach() {
740
765
  clearInterval(interval);
766
+ if (pinchMoveFrame !== null) {
767
+ cancelAnimationFrame(pinchMoveFrame);
768
+ pinchMoveFrame = null;
769
+ }
741
770
  }
742
771
  return { attach, detach };
743
772
  }
@@ -1075,6 +1104,10 @@
1075
1104
  return document.documentElement.clientWidth;
1076
1105
  }
1077
1106
  function getWindowHeight() {
1107
+ const visualViewport = window.visualViewport;
1108
+ if (visualViewport) {
1109
+ return visualViewport.height * visualViewport.scale;
1110
+ }
1078
1111
  return document.documentElement.clientHeight;
1079
1112
  }
1080
1113
  function isElementArray(source) {
@@ -1107,6 +1140,12 @@
1107
1140
  this.loadAllFired = /* @__PURE__ */ new Set();
1108
1141
  this.syncedGroupId = null;
1109
1142
  // ---- 内部: window イベント ----
1143
+ this.updateViewportHeight = () => {
1144
+ this.view.refs.dialog.style.setProperty(
1145
+ "--smartphoto-vh",
1146
+ `${getWindowHeight()}px`
1147
+ );
1148
+ };
1110
1149
  this.handleResize = () => {
1111
1150
  if (!currentItems(this.state)) {
1112
1151
  return;
@@ -1186,6 +1225,18 @@
1186
1225
  this.openPhoto(restored, null);
1187
1226
  }
1188
1227
  }
1228
+ this.updateViewportHeight();
1229
+ if (window.visualViewport) {
1230
+ window.visualViewport.addEventListener(
1231
+ "resize",
1232
+ this.updateViewportHeight,
1233
+ { signal: this.abortController.signal }
1234
+ );
1235
+ } else {
1236
+ window.addEventListener("resize", this.updateViewportHeight, {
1237
+ signal: this.abortController.signal
1238
+ });
1239
+ }
1189
1240
  if (!this.isSmartPhoneFlag) {
1190
1241
  window.addEventListener("resize", this.handleResize, {
1191
1242
  signal: this.abortController.signal
@@ -1494,6 +1545,10 @@
1494
1545
  }
1495
1546
  openPhotoWithViewTransition(trigger) {
1496
1547
  var _a, _b;
1548
+ document.documentElement.style.setProperty(
1549
+ "--smartphoto-animation-speed",
1550
+ `${this.state.options.animationSpeed}ms`
1551
+ );
1497
1552
  const transitionName = "smartphoto-hero";
1498
1553
  const thumbImg = (_a = trigger == null ? void 0 : trigger.querySelector("img")) != null ? _a : null;
1499
1554
  if (thumbImg) {