smartphoto 2.1.12 → 2.1.14

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
@@ -181,6 +181,11 @@ Slide fields:
181
181
  <td>update the URL hash (<code>#group=…&photo=…</code>) via the History API</td>
182
182
  <td>true</td>
183
183
  </tr>
184
+ <tr>
185
+ <td>useViewTransitionApi</td>
186
+ <td>use the View Transition API for the open animation when the browser supports it (falls back to the clone-based animation when disabled or unsupported)</td>
187
+ <td>true</td>
188
+ </tr>
184
189
  <tr>
185
190
  <td>swipeTopToClose</td>
186
191
  <td>close the viewer on an upward swipe</td>
@@ -132,6 +132,17 @@ dialog.smartphoto:not([open]) {
132
132
 
133
133
  .smartphoto::backdrop {
134
134
  background-color: var(--smartphoto-backdrop-color, rgba(0, 0, 0, 1));
135
+ /* iOS 26 Safari の「タブバー(下)」表示では、ページ本体はツールバーの背後
136
+ * (レイアウトビューポートの下端より下)までエッジツーエッジで描画されるのに対し、
137
+ * 既定の ::backdrop はビューポート下端で止まるため、サムネイルバーの下の帯から
138
+ * 裏のページが覗いてしまう。dialog 本体の高さを伸ばす方法は取らない
139
+ * (bottom:0 のサムネイルバーごとツールバーの背後に潜ってしまう)。
140
+ * レイアウトに影響しない ::backdrop 側だけを 100lvh + 下側セーフエリアまで
141
+ * 明示的に広げて帯を覆う。lvh 非対応ブラウザでは height 宣言ごと無効になり、
142
+ * 下の inset: 0 による従来のビューポート全面カバーにフォールバックする */
143
+ position: fixed;
144
+ inset: 0;
145
+ height: calc(100lvh + env(safe-area-inset-bottom, 0px));
135
146
  }
136
147
 
137
148
  /* View Transition(openPhotoWithViewTransition() 参照)の疑似要素ツリーは document の
@@ -146,6 +157,19 @@ dialog.smartphoto:not([open]) {
146
157
  animation-timing-function: var(--smartphoto-animation-function, ease-out);
147
158
  }
148
159
 
160
+ /* view-transition-name を付けなかった要素(サムネイル一覧のページ全体や、
161
+ * 新しく開く dialog の背景/矢印/キャプション等)は、既定では ::view-transition-*(root)
162
+ * が「開く前のページ」→「dialog が開いた後のページ」を丸ごとクロスフェードする。
163
+ * このクロスフェードは既定で mix-blend-mode: plus-lighter が掛かり、背景色が
164
+ * 大きく変わる(明るいページ→黒背景)場合に一瞬白っぽく光って見えたり、写真本体
165
+ * (smartphoto-hero)のモーフィングと重なって「一瞬消えてからまた出てくる」ように
166
+ * 見える原因になる。root 側のアニメーションは無効化し、写真の拡大だけを見せる */
167
+ ::view-transition-old(root),
168
+ ::view-transition-new(root) {
169
+ animation: none;
170
+ mix-blend-mode: normal;
171
+ }
172
+
149
173
  .smartphoto-close {
150
174
  opacity: 0;
151
175
  }
@@ -1 +1 @@
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,.45s) var(--smartphoto-animation-function,ease-out),display var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out) allow-discrete,overlay var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out) allow-discrete}.smartphoto:not([open]){opacity:0}dialog.smartphoto:not([open]){display:none!important}.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,.45s);animation-timing-function:var(--smartphoto-animation-function,ease-out)}.smartphoto-close{opacity:0}.smartphoto-count{display:inline-block;color:var(--smartphoto-icon-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:var(--smartphoto-icon-color,#fff);mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHZpZXdCb3g9IjAgMCAyODM0LjY1IDI4MzQuNjUiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNMTU3Ni40MiAxNDA2Ljc2IDI3ODQgMTk5LjE5YTU2Ljg2IDU2Ljg2IDAgMCAwIDAtODAuMThsLTc4LjkyLTc4LjkyYTU2Ljg2IDU2Ljg2IDAgMCAwLTgwLjE4IDBMMTQxNy4zMiAxMjQ3LjY2IDIwOS43NSA0MC4wOWE1Ni44NiA1Ni44NiAwIDAgMC04MC4xOCAwTDUwLjY1IDExOWE1Ni44NiA1Ni44NiAwIDAgMCAwIDgwLjE4bDEyMDcuNTggMTIwNy41OEw1MC42NSAyNjE0LjM0YTU2Ljg2IDU2Ljg2IDAgMCAwIDAgODAuMThsNzguOTIgNzguOTJhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGwxMjA3LjU3LTEyMDcuNThMMjYyNC45IDI3NzMuNDRhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGw3OC45Mi03OC45MmE1Ni44NiA1Ni44NiAwIDAgMCAwLTgwLjE4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHZpZXdCb3g9IjAgMCAyODM0LjY1IDI4MzQuNjUiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNMTU3Ni40MiAxNDA2Ljc2IDI3ODQgMTk5LjE5YTU2Ljg2IDU2Ljg2IDAgMCAwIDAtODAuMThsLTc4LjkyLTc4LjkyYTU2Ljg2IDU2Ljg2IDAgMCAwLTgwLjE4IDBMMTQxNy4zMiAxMjQ3LjY2IDIwOS43NSA0MC4wOWE1Ni44NiA1Ni44NiAwIDAgMC04MC4xOCAwTDUwLjY1IDExOWE1Ni44NiA1Ni44NiAwIDAgMCAwIDgwLjE4bDEyMDcuNTggMTIwNy41OEw1MC42NSAyNjE0LjM0YTU2Ljg2IDU2Ljg2IDAgMCAwIDAgODAuMThsNzguOTIgNzguOTJhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGwxMjA3LjU3LTEyMDcuNThMMjYyNC45IDI3NzMuNDRhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGw3OC45Mi03OC45MmE1Ni44NiA1Ni44NiAwIDAgMCAwLTgwLjE4WiIvPjwvc3ZnPg==);mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;mask-size:contain;-webkit-mask-size:contain;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,.45s) 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,.45s) var(--smartphoto-animation-function,ease-out)}.smartphoto-img-wrap{display:inline-block;opacity:1;touch-action:none;-webkit-transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);-moz-transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);-ms-transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);-o-transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);animation-name:smartphoto-img-wrap;animation-duration:var(--smartphoto-animation-speed,.45s);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,.45s);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:var(--smartphoto-arrow-top,50%);width:30px;height:30px;margin-top:-20px;box-sizing:content-box;animation-duration:var(--smartphoto-animation-speed,.45s);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%;padding-bottom:env(safe-area-inset-bottom,0);box-sizing:border-box;opacity:1;animation-name:smartphoto-appear;animation-duration:var(--smartphoto-animation-speed,.45s);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,.45s) 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:var(--smartphoto-icon-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,.45s) 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,.45s) var(--smartphoto-animation-function,ease-out),display var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out) allow-discrete,overlay var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out) allow-discrete}.smartphoto:not([open]){opacity:0}dialog.smartphoto:not([open]){display:none!important}.smartphoto::backdrop{background-color:var(--smartphoto-backdrop-color,#000);position:fixed;inset:0;height:calc(100lvh + env(safe-area-inset-bottom, 0px))}::view-transition-group(smartphoto-hero),::view-transition-new(smartphoto-hero),::view-transition-old(smartphoto-hero){animation-duration:var(--smartphoto-animation-speed,.45s);animation-timing-function:var(--smartphoto-animation-function,ease-out)}::view-transition-new(root),::view-transition-old(root){animation:none;mix-blend-mode:normal}.smartphoto-close{opacity:0}.smartphoto-count{display:inline-block;color:var(--smartphoto-icon-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:var(--smartphoto-icon-color,#fff);mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHZpZXdCb3g9IjAgMCAyODM0LjY1IDI4MzQuNjUiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNMTU3Ni40MiAxNDA2Ljc2IDI3ODQgMTk5LjE5YTU2Ljg2IDU2Ljg2IDAgMCAwIDAtODAuMThsLTc4LjkyLTc4LjkyYTU2Ljg2IDU2Ljg2IDAgMCAwLTgwLjE4IDBMMTQxNy4zMiAxMjQ3LjY2IDIwOS43NSA0MC4wOWE1Ni44NiA1Ni44NiAwIDAgMC04MC4xOCAwTDUwLjY1IDExOWE1Ni44NiA1Ni44NiAwIDAgMCAwIDgwLjE4bDEyMDcuNTggMTIwNy41OEw1MC42NSAyNjE0LjM0YTU2Ljg2IDU2Ljg2IDAgMCAwIDAgODAuMThsNzguOTIgNzguOTJhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGwxMjA3LjU3LTEyMDcuNThMMjYyNC45IDI3NzMuNDRhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGw3OC45Mi03OC45MmE1Ni44NiA1Ni44NiAwIDAgMCAwLTgwLjE4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIGRhdGEtbmFtZT0i44Os44Kk44Ok44O8IDEiIHZpZXdCb3g9IjAgMCAyODM0LjY1IDI4MzQuNjUiPjx0aXRsZT5pY29uPC90aXRsZT48cGF0aCBkPSJNMTU3Ni40MiAxNDA2Ljc2IDI3ODQgMTk5LjE5YTU2Ljg2IDU2Ljg2IDAgMCAwIDAtODAuMThsLTc4LjkyLTc4LjkyYTU2Ljg2IDU2Ljg2IDAgMCAwLTgwLjE4IDBMMTQxNy4zMiAxMjQ3LjY2IDIwOS43NSA0MC4wOWE1Ni44NiA1Ni44NiAwIDAgMC04MC4xOCAwTDUwLjY1IDExOWE1Ni44NiA1Ni44NiAwIDAgMCAwIDgwLjE4bDEyMDcuNTggMTIwNy41OEw1MC42NSAyNjE0LjM0YTU2Ljg2IDU2Ljg2IDAgMCAwIDAgODAuMThsNzguOTIgNzguOTJhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGwxMjA3LjU3LTEyMDcuNThMMjYyNC45IDI3NzMuNDRhNTYuODYgNTYuODYgMCAwIDAgODAuMTggMGw3OC45Mi03OC45MmE1Ni44NiA1Ni44NiAwIDAgMCAwLTgwLjE4WiIvPjwvc3ZnPg==);mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat;mask-size:contain;-webkit-mask-size:contain;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,.45s) 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,.45s) var(--smartphoto-animation-function,ease-out)}.smartphoto-img-wrap{display:inline-block;opacity:1;touch-action:none;-webkit-transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);-moz-transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);-ms-transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);-o-transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);transition:opacity var(--smartphoto-animation-speed,.45s) var(--smartphoto-animation-function,ease-out);animation-name:smartphoto-img-wrap;animation-duration:var(--smartphoto-animation-speed,.45s);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,.45s);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:var(--smartphoto-arrow-top,50%);width:30px;height:30px;margin-top:-20px;box-sizing:content-box;animation-duration:var(--smartphoto-animation-speed,.45s);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%;padding-bottom:env(safe-area-inset-bottom,0);box-sizing:border-box;opacity:1;animation-name:smartphoto-appear;animation-duration:var(--smartphoto-animation-speed,.45s);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,.45s) 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:var(--smartphoto-icon-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,.45s) 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.12
2
+ * SmartPhoto v2.1.14
3
3
  * (c) appleple
4
4
  * Released under the MIT License.
5
5
  */
@@ -10,6 +10,9 @@
10
10
  const agent = navigator.userAgent;
11
11
  return agent.indexOf("iPhone") > 0 || agent.indexOf("iPad") > 0 || agent.indexOf("ipod") > 0 || agent.indexOf("Android") > 0;
12
12
  };
13
+ var isWebKit = () => {
14
+ return typeof window.webkitConvertPointFromNodeToPage === "function";
15
+ };
13
16
  function deepExtend(out, ...args) {
14
17
  var _a;
15
18
  out = out || {};
@@ -148,6 +151,7 @@
148
151
  verticalGravity: false,
149
152
  useOrientationApi: false,
150
153
  useHistoryApi: true,
154
+ useViewTransitionApi: true,
151
155
  swipeTopToClose: false,
152
156
  swipeBottomToClose: true,
153
157
  swipeOffset: 100,
@@ -435,6 +439,7 @@
435
439
  let oldPos = null;
436
440
  let moveDir = null;
437
441
  let swipeStartTime = 0;
442
+ let tapTarget = null;
438
443
  let photoSwipable = false;
439
444
  let firstPhotoPos = null;
440
445
  let oldPhotoPos = null;
@@ -565,6 +570,7 @@
565
570
  firstPos = pos;
566
571
  oldPos = pos;
567
572
  swipeStartTime = Date.now();
573
+ tapTarget = e.target;
568
574
  }
569
575
  function startPhotoDrag(e) {
570
576
  photoSwipable = true;
@@ -706,11 +712,11 @@
706
712
  const swipeHeight = last.y - first.y;
707
713
  const noMove = swipeWidth === 0 && swipeHeight === 0;
708
714
  if (!isSmartPhone2() && noMove) {
709
- callbacks.onTap();
715
+ callbacks.onTap(tapTarget);
710
716
  return;
711
717
  }
712
718
  if (Math.abs(offset) <= 500 && noMove) {
713
- callbacks.onTap();
719
+ callbacks.onTap(tapTarget);
714
720
  return;
715
721
  }
716
722
  tapSecond = now;
@@ -1035,6 +1041,9 @@
1035
1041
  if (slideRefs.imgWrap && slideRefs.img) {
1036
1042
  slideRefs.imgWrap.style.transform = `translate(${item.x}px,${item.y}px) scale(${item.scale})`;
1037
1043
  slideRefs.img.style.width = `${item.width}px`;
1044
+ if (!isCurrent) {
1045
+ slideRefs.img.style.transform = "translate(0px,0px) scale(1)";
1046
+ }
1038
1047
  slideRefs.img.classList.toggle("active", viewer.appear);
1039
1048
  slideRefs.img.classList.toggle(
1040
1049
  classNames.smartPhotoImgOnMove,
@@ -1118,6 +1127,32 @@
1118
1127
  refs.list.style.transform = `translate(${viewer.translateX}px,${viewer.translateY}px)`;
1119
1128
  refs.list.classList.toggle(classNames.smartPhotoListOnMove, viewer.onMove);
1120
1129
  }
1130
+ function applyViewTransitionLayout(state) {
1131
+ for (const [item, slideRefs] of refs.slides) {
1132
+ if (item.index !== state.viewer.currentIndex) {
1133
+ continue;
1134
+ }
1135
+ const { img, imgWrap } = slideRefs;
1136
+ if (!img || !imgWrap || !item.width || !item.scale || item.scale >= 1) {
1137
+ return;
1138
+ }
1139
+ const before = img.getBoundingClientRect();
1140
+ img.style.width = `${item.width * item.scale}px`;
1141
+ imgWrap.style.transform = `translate(${item.x}px,${item.y}px) scale(1)`;
1142
+ const after = img.getBoundingClientRect();
1143
+ imgWrap.style.transform = `translate(${item.x + before.left - after.left}px,${item.y + before.top - after.top}px) scale(1)`;
1144
+ return;
1145
+ }
1146
+ }
1147
+ function resetViewTransitionLayout() {
1148
+ for (const [item, slideRefs] of refs.slides) {
1149
+ const { img, imgWrap } = slideRefs;
1150
+ if (img && imgWrap) {
1151
+ imgWrap.style.transform = `translate(${item.x}px,${item.y}px) scale(${item.scale})`;
1152
+ img.style.width = `${item.width}px`;
1153
+ }
1154
+ }
1155
+ }
1121
1156
  function showAppearEffect(effect) {
1122
1157
  const clone = document.createElement("img");
1123
1158
  clone.className = classNames.smartPhotoImgClone;
@@ -1142,6 +1177,8 @@
1142
1177
  render,
1143
1178
  syncSlides,
1144
1179
  updatePhotoTransform,
1180
+ applyViewTransitionLayout,
1181
+ resetViewTransitionLayout,
1145
1182
  updateListTransform,
1146
1183
  showAppearEffect,
1147
1184
  removeAppearEffect,
@@ -1178,6 +1215,13 @@
1178
1215
  // doHideEffect() の後始末(画像に設定した translateY の除去と promise 解決)を
1179
1216
  // transitionend を待たずに即時実行するための関数。後始末が済むと null に戻る
1180
1217
  this.finishHideEffect = null;
1218
+ // 開く View Transition が進行中かどうか。進行中にリサイズ由来の再計算(commit)が
1219
+ // 走ると、モーフの目標位置は古いビューポート基準のままなのに実要素だけ新しい
1220
+ // 位置へ動いて「一旦ずれた位置に出てから正位置へ戻る」ように見え、さらに
1221
+ // モーフ用レイアウト(applyViewTransitionLayout)も巻き戻されてしまう。
1222
+ // そのため進行中はリサイズ再計算を保留し、finished 後にまとめて実行する
1223
+ this.isViewTransitionActive = false;
1224
+ this.pendingViewTransitionResync = false;
1181
1225
  this.timeouts = [];
1182
1226
  this.loadAllFired = /* @__PURE__ */ new Set();
1183
1227
  this.syncedGroupId = null;
@@ -1236,16 +1280,24 @@
1236
1280
  if (!this.isSmartPhoneFlag || !this.state.viewer.isOpen || !currentItems(this.state)) {
1237
1281
  return;
1238
1282
  }
1283
+ if (this.isViewTransitionActive) {
1284
+ this.pendingViewTransitionResync = true;
1285
+ return;
1286
+ }
1239
1287
  this.resetTranslateCurrent();
1240
1288
  this.setPosByCurrentIndex();
1241
1289
  this.setSizeByScreen();
1242
1290
  this.commit();
1243
1291
  };
1244
1292
  this.handleResize = () => {
1245
- if (!currentItems(this.state)) {
1293
+ if (!this.state.viewer.isOpen || !currentItems(this.state)) {
1246
1294
  return;
1247
1295
  }
1248
1296
  this.updateViewportHeight();
1297
+ if (this.isViewTransitionActive) {
1298
+ this.pendingViewTransitionResync = true;
1299
+ return;
1300
+ }
1249
1301
  this.resetTranslateCurrent();
1250
1302
  this.setPosByCurrentIndex();
1251
1303
  this.setSizeByScreen();
@@ -1265,7 +1317,11 @@
1265
1317
  }
1266
1318
  };
1267
1319
  this.handleOrientationChange = () => {
1268
- if (!currentItems(this.state)) {
1320
+ if (!this.state.viewer.isOpen || !currentItems(this.state)) {
1321
+ return;
1322
+ }
1323
+ if (this.isViewTransitionActive) {
1324
+ this.pendingViewTransitionResync = true;
1269
1325
  return;
1270
1326
  }
1271
1327
  this.updateViewportHeight();
@@ -1278,6 +1334,9 @@
1278
1334
  const timeout = 500;
1279
1335
  const poll = (time) => {
1280
1336
  this.scheduleTimeout(() => {
1337
+ if (!this.state.viewer.isOpen) {
1338
+ return;
1339
+ }
1281
1340
  if (prevWidth !== getWindowWidth2()) {
1282
1341
  this.updateViewportHeight();
1283
1342
  this.resetTranslateCurrent();
@@ -1376,10 +1435,14 @@
1376
1435
  this.destroy();
1377
1436
  }
1378
1437
  gotoSlide(index) {
1379
- this.state.viewer.currentIndex = Number.parseInt(String(index), 10);
1380
- if (!this.state.viewer.currentIndex) {
1381
- this.state.viewer.currentIndex = 0;
1438
+ var _a, _b;
1439
+ const parsed = Number.parseInt(String(index), 10);
1440
+ const next = Number.isNaN(parsed) ? 0 : parsed;
1441
+ const total = (_b = (_a = currentItems(this.state)) == null ? void 0 : _a.length) != null ? _b : 0;
1442
+ if (next < 0 || next >= total) {
1443
+ return;
1382
1444
  }
1445
+ this.state.viewer.currentIndex = next;
1383
1446
  this.slideList();
1384
1447
  }
1385
1448
  hidePhoto(dir = "bottom") {
@@ -1430,10 +1493,26 @@
1430
1493
  this.state.viewer.photoPosY = 0;
1431
1494
  this.view.updatePhotoTransform(this.state);
1432
1495
  this.scheduleTimeout(() => {
1496
+ if (!this.state.viewer.isOpen) {
1497
+ return;
1498
+ }
1433
1499
  this.state.viewer.scale = true;
1434
1500
  this.view.updatePhotoTransform(this.state);
1435
1501
  this.fireEvent("zoomin");
1436
- }, 300);
1502
+ }, this.state.options.animationSpeed);
1503
+ }
1504
+ // zoomPhoto() と同じ「ズームの余地があるか」の判定(scaleBorder <= 1 なら
1505
+ // ズームしても見た目が変わらない)。zoomPhoto() 自体は公開 API の契約
1506
+ // (ズームできなければ何もしない)を保つ必要があるため、タップの
1507
+ // zoom-or-close 分岐(§7)用に判定だけを切り出している
1508
+ canZoomCurrentPhoto() {
1509
+ const item = currentItem(this.state);
1510
+ return !!item && scaleBorder(
1511
+ item,
1512
+ getWindowWidth2(),
1513
+ getWindowHeight2(),
1514
+ this.isSmartPhoneFlag
1515
+ ) > 1;
1437
1516
  }
1438
1517
  zoomOutPhoto() {
1439
1518
  this.state.viewer.scaleSize = 1;
@@ -1644,6 +1723,7 @@
1644
1723
  this.state.options.headerHeight,
1645
1724
  this.state.options.footerHeight
1646
1725
  );
1726
+ this.syncFillScale();
1647
1727
  }
1648
1728
  // resize/orientationchange ハンドラは呼び出し前に currentItems の存在を確認済み
1649
1729
  resetTranslateCurrent() {
@@ -1657,9 +1737,18 @@
1657
1737
  }
1658
1738
  return null;
1659
1739
  }
1740
+ currentLiElement() {
1741
+ for (const [item, slideRefs] of this.view.refs.slides) {
1742
+ if (item.index === this.state.viewer.currentIndex) {
1743
+ return slideRefs.li;
1744
+ }
1745
+ }
1746
+ return null;
1747
+ }
1660
1748
  // ---- 内部: dialog 制御(§8) ----
1661
1749
  syncDialog() {
1662
1750
  const { dialog, caption } = this.view.refs;
1751
+ dialog.style.transition = this.state.options.showAnimation === false ? "none" : "";
1663
1752
  if (this.state.viewer.isOpen && !dialog.open) {
1664
1753
  dialog.style.removeProperty("display");
1665
1754
  dialog.showModal();
@@ -1671,6 +1760,7 @@
1671
1760
  }
1672
1761
  commit() {
1673
1762
  this.view.render(this.state);
1763
+ this.view.updatePhotoTransform(this.state);
1674
1764
  this.syncDialog();
1675
1765
  }
1676
1766
  // ---- 内部: 開く演出 ----
@@ -1689,20 +1779,34 @@
1689
1779
  this.setPosByCurrentIndex();
1690
1780
  this.setSizeByScreen();
1691
1781
  setArrow(this.state);
1692
- if (this.state.options.resizeStyle === "fill" && this.isSmartPhoneFlag) {
1693
- const item = currentItem(this.state);
1694
- this.state.viewer.scale = true;
1695
- this.state.viewer.hideUi = true;
1696
- this.state.viewer.scaleSize = scaleBorder(
1697
- item,
1698
- getWindowWidth2(),
1699
- getWindowHeight2(),
1700
- this.isSmartPhoneFlag
1701
- );
1782
+ }
1783
+ // resizeStyle: 'fill' はスマートフォンでのみ、画面を隙間なく覆うよう
1784
+ // scaleBorder() の倍率を viewer.scaleSize に適用する。fit用の item.scale に
1785
+ // 依存するため、setSizeByScreen() の末尾から必ず呼ばれる(個別に呼ぶ必要はない)
1786
+ syncFillScale() {
1787
+ if (this.state.options.resizeStyle !== "fill" || !this.isSmartPhoneFlag) {
1788
+ return;
1702
1789
  }
1790
+ const item = currentItem(this.state);
1791
+ if (!item) {
1792
+ return;
1793
+ }
1794
+ this.state.viewer.scale = true;
1795
+ this.state.viewer.hideUi = true;
1796
+ this.state.viewer.scaleSize = scaleBorder(
1797
+ item,
1798
+ getWindowWidth2(),
1799
+ getWindowHeight2(),
1800
+ this.isSmartPhoneFlag
1801
+ );
1703
1802
  }
1704
1803
  supportsViewTransition() {
1705
- return typeof document.startViewTransition === "function";
1804
+ return this.state.options.useViewTransitionApi && typeof document.startViewTransition === "function" && // WebKit には「view-transition-name 付き要素が root スナップショットから
1805
+ // 除外されず、モーフ中に最終位置へ二重描画される」実装バグがあり、開く演出が
1806
+ // 「一旦ずれた位置に出てから正位置へ戻る」ように乱れる。API 対応の有無では
1807
+ // 検出できないため、WebKit ではクローン演出(addAppearEffect)へフォールバック
1808
+ // する(§util.isWebKit)
1809
+ !isWebKit();
1706
1810
  }
1707
1811
  openPhotoWithViewTransition(trigger) {
1708
1812
  var _a, _b;
@@ -1715,7 +1819,8 @@
1715
1819
  if (thumbImg) {
1716
1820
  thumbImg.style.viewTransitionName = transitionName;
1717
1821
  }
1718
- const clearNames = () => {
1822
+ const cleanup = () => {
1823
+ this.isViewTransitionActive = false;
1719
1824
  if (thumbImg) {
1720
1825
  thumbImg.style.viewTransitionName = "";
1721
1826
  }
@@ -1723,6 +1828,21 @@
1723
1828
  if (img) {
1724
1829
  img.style.viewTransitionName = "";
1725
1830
  }
1831
+ if (!this.state.viewer.isOpen) {
1832
+ this.pendingViewTransitionResync = false;
1833
+ return;
1834
+ }
1835
+ if (this.pendingViewTransitionResync && currentItems(this.state)) {
1836
+ this.pendingViewTransitionResync = false;
1837
+ this.updateViewportHeight();
1838
+ this.resetTranslateCurrent();
1839
+ this.setPosByCurrentIndex();
1840
+ this.setSizeByScreen();
1841
+ this.commit();
1842
+ return;
1843
+ }
1844
+ this.pendingViewTransitionResync = false;
1845
+ this.view.resetViewTransitionLayout();
1726
1846
  };
1727
1847
  const transition = (_b = document.startViewTransition) == null ? void 0 : _b.call(document, () => {
1728
1848
  this.initPhoto();
@@ -1732,11 +1852,16 @@
1732
1852
  thumbImg.style.viewTransitionName = "";
1733
1853
  }
1734
1854
  this.currentImgElement().style.viewTransitionName = transitionName;
1855
+ this.view.applyViewTransitionLayout(this.state);
1735
1856
  });
1736
- transition == null ? void 0 : transition.ready.catch(() => {
1737
- clearNames();
1857
+ if (!transition) {
1858
+ return;
1859
+ }
1860
+ this.isViewTransitionActive = true;
1861
+ transition.ready.catch(() => {
1862
+ cleanup();
1738
1863
  });
1739
- transition == null ? void 0 : transition.finished.then(clearNames, clearNames);
1864
+ transition.finished.then(cleanup, cleanup);
1740
1865
  }
1741
1866
  // doOpen() の呼び出し元でこの分岐に来る時点で showAnimation !== false が保証されている
1742
1867
  addAppearEffect(trigger, item) {
@@ -1846,10 +1971,14 @@
1846
1971
  if (getWindowWidth2() === winWidthBefore && getWindowHeight2() === winHeightBefore) {
1847
1972
  return;
1848
1973
  }
1974
+ if (this.isViewTransitionActive) {
1975
+ this.pendingViewTransitionResync = true;
1976
+ return;
1977
+ }
1849
1978
  this.resetTranslateCurrent();
1850
1979
  this.setPosByCurrentIndex();
1851
1980
  this.setSizeByScreen();
1852
- this.view.render(this.state);
1981
+ this.commit();
1853
1982
  });
1854
1983
  }
1855
1984
  openPhoto(item, trigger) {
@@ -1871,21 +2000,37 @@
1871
2000
  }
1872
2001
  doHideEffect(dir) {
1873
2002
  return new Promise((resolve) => {
2003
+ if (this.state.options.showAnimation === false) {
2004
+ resolve();
2005
+ return;
2006
+ }
1874
2007
  const dialog = this.view.refs.dialog;
1875
- const img = this.currentImgElement();
2008
+ const item = currentItem(this.state);
2009
+ const li = item ? this.currentLiElement() : null;
1876
2010
  const height = getWindowHeight2();
1877
- const applied = dir === "top" ? `translateY(-${height}px)` : `translateY(${height}px)`;
2011
+ const img = this.currentImgElement();
2012
+ let frozenTransform = null;
1878
2013
  if (img) {
1879
- img.style.transform = applied;
2014
+ const rendered = getComputedStyle(img).transform;
2015
+ if (rendered && rendered !== "none") {
2016
+ img.style.transform = rendered;
2017
+ frozenTransform = img.style.transform;
2018
+ }
1880
2019
  }
1881
- const stored = img ? img.style.transform : "";
1882
- const finish = () => {
2020
+ if (li && item) {
2021
+ const offsetY = dir === "top" ? -height : height;
2022
+ li.style.transform = `translate(${item.translateX}px,${item.translateY + offsetY}px)`;
2023
+ }
2024
+ const finish = (e) => {
1883
2025
  if (this.finishHideEffect !== finish) {
1884
2026
  return;
1885
2027
  }
2028
+ if (e && e.target !== dialog) {
2029
+ return;
2030
+ }
1886
2031
  this.finishHideEffect = null;
1887
2032
  dialog.removeEventListener("transitionend", finish, true);
1888
- if (img && img.style.transform === stored) {
2033
+ if (img && frozenTransform && img.style.transform === frozenTransform) {
1889
2034
  img.style.transform = "";
1890
2035
  }
1891
2036
  resolve();
@@ -1961,7 +2106,9 @@
1961
2106
  const item = currentItem(this.state);
1962
2107
  this.state.viewer.onMove = false;
1963
2108
  setArrow(this.state);
1964
- this.commit();
2109
+ if (this.state.viewer.isOpen) {
2110
+ this.commit();
2111
+ }
1965
2112
  if (this.state.viewer.oldIndex !== this.state.viewer.currentIndex) {
1966
2113
  this.fireEvent("change");
1967
2114
  }
@@ -2046,7 +2193,21 @@
2046
2193
  }
2047
2194
  this.slideList();
2048
2195
  },
2049
- onTap: () => this.zoomPhoto(),
2196
+ // 主要ライトボックスのデファクトスタンダード(PhotoSwipe: bgClickAction
2197
+ // 'close' / imageClickAction 'zoom-or-close'、Fancybox: backdropClick
2198
+ // 'close'、GLightbox: closeOnOutsideClick、lightGallery: closable)に合わせ、
2199
+ // 写真の上のタップはズーム、写真の外(背景)のタップは閉じる。ズームの
2200
+ // 余地がない(scaleBorder <= 1 で zoomPhoto() が何もしない)写真の上の
2201
+ // タップも、無反応にせず 'zoom-or-close' に倣って閉じる
2202
+ onTap: (target) => {
2203
+ const img = this.currentImgElement();
2204
+ const onPhoto = !!img && target instanceof Node && img.contains(target);
2205
+ if (onPhoto && this.canZoomCurrentPhoto()) {
2206
+ this.zoomPhoto();
2207
+ } else {
2208
+ this.hidePhoto();
2209
+ }
2210
+ },
2050
2211
  onGestureStart: () => {
2051
2212
  this.fireEvent("gesturestart");
2052
2213
  this.view.updatePhotoTransform(this.state);