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/lib/smartphoto.js CHANGED
@@ -124,6 +124,7 @@ var defaults = {
124
124
  swipeTopToClose: false,
125
125
  swipeBottomToClose: true,
126
126
  swipeOffset: 100,
127
+ swipeVelocity: 0.5,
127
128
  headerHeight: 60,
128
129
  footerHeight: 60,
129
130
  forceInterval: 10,
@@ -381,6 +382,7 @@ function distance(p1, p2) {
381
382
  const y = p1.y - p2.y;
382
383
  return Math.sqrt(x * x + y * y);
383
384
  }
385
+ var MIN_FLICK_DISTANCE = 10;
384
386
  function getForceAndTheta(x, y) {
385
387
  return { force: Math.sqrt(x * x + y * y), theta: Math.atan2(y, x) };
386
388
  }
@@ -398,6 +400,7 @@ function createGestures({ state, callbacks }, { signal }) {
398
400
  let firstPos = null;
399
401
  let oldPos = null;
400
402
  let moveDir = null;
403
+ let swipeStartTime = 0;
401
404
  let photoSwipable = false;
402
405
  let firstPhotoPos = null;
403
406
  let oldPhotoPos = null;
@@ -405,6 +408,7 @@ function createGestures({ state, callbacks }, { signal }) {
405
408
  let photoVY = 0;
406
409
  let pinching = false;
407
410
  let oldDistance = 0;
411
+ let pinchMoveFrame = null;
408
412
  let vx = 0;
409
413
  let vy = 0;
410
414
  function isSmartPhone2() {
@@ -526,6 +530,7 @@ function createGestures({ state, callbacks }, { signal }) {
526
530
  dragStart = true;
527
531
  firstPos = pos;
528
532
  oldPos = pos;
533
+ swipeStartTime = Date.now();
529
534
  }
530
535
  function startPhotoDrag(e) {
531
536
  photoSwipable = true;
@@ -549,6 +554,23 @@ function createGestures({ state, callbacks }, { signal }) {
549
554
  }
550
555
  startSwipe(e);
551
556
  }
557
+ function scheduleGestureMove() {
558
+ if (pinchMoveFrame !== null) {
559
+ return;
560
+ }
561
+ pinchMoveFrame = requestAnimationFrame(() => {
562
+ pinchMoveFrame = null;
563
+ callbacks.onGestureMove();
564
+ });
565
+ }
566
+ function flushGestureMove() {
567
+ if (pinchMoveFrame === null) {
568
+ return;
569
+ }
570
+ cancelAnimationFrame(pinchMoveFrame);
571
+ pinchMoveFrame = null;
572
+ callbacks.onGestureMove();
573
+ }
552
574
  function movePinch() {
553
575
  const points = Array.from(activePointers.values());
554
576
  const dist = distance(
@@ -573,7 +595,7 @@ function createGestures({ state, callbacks }, { signal }) {
573
595
  state.viewer.hideUi = state.viewer.scaleSize < 1 || state.viewer.scaleSize > border;
574
596
  }
575
597
  oldDistance = dist;
576
- callbacks.onGestureMove();
598
+ scheduleGestureMove();
577
599
  }
578
600
  function moveSwipe(e) {
579
601
  const pos = getPos(e);
@@ -622,6 +644,7 @@ function createGestures({ state, callbacks }, { signal }) {
622
644
  }
623
645
  function endPinch() {
624
646
  pinching = false;
647
+ flushGestureMove();
625
648
  const item = currentItem(state);
626
649
  if (!item) {
627
650
  return;
@@ -658,9 +681,11 @@ function createGestures({ state, callbacks }, { signal }) {
658
681
  const items = currentItems(state) ?? [];
659
682
  if (moveDir === "horizontal") {
660
683
  let result = "stay";
661
- if (swipeWidth >= state.options.swipeOffset && state.viewer.currentIndex !== 0) {
684
+ const elapsedMs = Math.max(now - swipeStartTime, 1);
685
+ const isFlick = Math.abs(swipeWidth) >= MIN_FLICK_DISTANCE && Math.abs(swipeWidth) / elapsedMs >= state.options.swipeVelocity;
686
+ if ((swipeWidth >= state.options.swipeOffset || isFlick && swipeWidth > 0) && state.viewer.currentIndex !== 0) {
662
687
  result = "prev";
663
- } else if (swipeWidth <= -state.options.swipeOffset && state.viewer.currentIndex !== items.length - 1) {
688
+ } else if ((swipeWidth <= -state.options.swipeOffset || isFlick && swipeWidth < 0) && state.viewer.currentIndex !== items.length - 1) {
664
689
  result = "next";
665
690
  }
666
691
  callbacks.onSwipeEnd(result);
@@ -751,6 +776,10 @@ function createGestures({ state, callbacks }, { signal }) {
751
776
  }
752
777
  function detach() {
753
778
  clearInterval(interval);
779
+ if (pinchMoveFrame !== null) {
780
+ cancelAnimationFrame(pinchMoveFrame);
781
+ pinchMoveFrame = null;
782
+ }
754
783
  }
755
784
  return { attach, detach };
756
785
  }
@@ -1082,6 +1111,10 @@ function getWindowWidth() {
1082
1111
  return document.documentElement.clientWidth;
1083
1112
  }
1084
1113
  function getWindowHeight() {
1114
+ const visualViewport = window.visualViewport;
1115
+ if (visualViewport) {
1116
+ return visualViewport.height * visualViewport.scale;
1117
+ }
1085
1118
  return document.documentElement.clientHeight;
1086
1119
  }
1087
1120
  function isElementArray(source) {
@@ -1114,6 +1147,12 @@ var SmartPhoto = class {
1114
1147
  this.loadAllFired = /* @__PURE__ */ new Set();
1115
1148
  this.syncedGroupId = null;
1116
1149
  // ---- 内部: window イベント ----
1150
+ this.updateViewportHeight = () => {
1151
+ this.view.refs.dialog.style.setProperty(
1152
+ "--smartphoto-vh",
1153
+ `${getWindowHeight()}px`
1154
+ );
1155
+ };
1117
1156
  this.handleResize = () => {
1118
1157
  if (!currentItems(this.state)) {
1119
1158
  return;
@@ -1193,6 +1232,18 @@ var SmartPhoto = class {
1193
1232
  this.openPhoto(restored, null);
1194
1233
  }
1195
1234
  }
1235
+ this.updateViewportHeight();
1236
+ if (window.visualViewport) {
1237
+ window.visualViewport.addEventListener(
1238
+ "resize",
1239
+ this.updateViewportHeight,
1240
+ { signal: this.abortController.signal }
1241
+ );
1242
+ } else {
1243
+ window.addEventListener("resize", this.updateViewportHeight, {
1244
+ signal: this.abortController.signal
1245
+ });
1246
+ }
1196
1247
  if (!this.isSmartPhoneFlag) {
1197
1248
  window.addEventListener("resize", this.handleResize, {
1198
1249
  signal: this.abortController.signal
@@ -1494,6 +1545,10 @@ var SmartPhoto = class {
1494
1545
  return typeof document.startViewTransition === "function";
1495
1546
  }
1496
1547
  openPhotoWithViewTransition(trigger) {
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 = trigger?.querySelector("img") ?? null;
1499
1554
  if (thumbImg) {
@@ -98,6 +98,7 @@ var defaults = {
98
98
  swipeTopToClose: false,
99
99
  swipeBottomToClose: true,
100
100
  swipeOffset: 100,
101
+ swipeVelocity: 0.5,
101
102
  headerHeight: 60,
102
103
  footerHeight: 60,
103
104
  forceInterval: 10,
@@ -355,6 +356,7 @@ function distance(p1, p2) {
355
356
  const y = p1.y - p2.y;
356
357
  return Math.sqrt(x * x + y * y);
357
358
  }
359
+ var MIN_FLICK_DISTANCE = 10;
358
360
  function getForceAndTheta(x, y) {
359
361
  return { force: Math.sqrt(x * x + y * y), theta: Math.atan2(y, x) };
360
362
  }
@@ -372,6 +374,7 @@ function createGestures({ state, callbacks }, { signal }) {
372
374
  let firstPos = null;
373
375
  let oldPos = null;
374
376
  let moveDir = null;
377
+ let swipeStartTime = 0;
375
378
  let photoSwipable = false;
376
379
  let firstPhotoPos = null;
377
380
  let oldPhotoPos = null;
@@ -379,6 +382,7 @@ function createGestures({ state, callbacks }, { signal }) {
379
382
  let photoVY = 0;
380
383
  let pinching = false;
381
384
  let oldDistance = 0;
385
+ let pinchMoveFrame = null;
382
386
  let vx = 0;
383
387
  let vy = 0;
384
388
  function isSmartPhone2() {
@@ -500,6 +504,7 @@ function createGestures({ state, callbacks }, { signal }) {
500
504
  dragStart = true;
501
505
  firstPos = pos;
502
506
  oldPos = pos;
507
+ swipeStartTime = Date.now();
503
508
  }
504
509
  function startPhotoDrag(e) {
505
510
  photoSwipable = true;
@@ -523,6 +528,23 @@ function createGestures({ state, callbacks }, { signal }) {
523
528
  }
524
529
  startSwipe(e);
525
530
  }
531
+ function scheduleGestureMove() {
532
+ if (pinchMoveFrame !== null) {
533
+ return;
534
+ }
535
+ pinchMoveFrame = requestAnimationFrame(() => {
536
+ pinchMoveFrame = null;
537
+ callbacks.onGestureMove();
538
+ });
539
+ }
540
+ function flushGestureMove() {
541
+ if (pinchMoveFrame === null) {
542
+ return;
543
+ }
544
+ cancelAnimationFrame(pinchMoveFrame);
545
+ pinchMoveFrame = null;
546
+ callbacks.onGestureMove();
547
+ }
526
548
  function movePinch() {
527
549
  const points = Array.from(activePointers.values());
528
550
  const dist = distance(
@@ -547,7 +569,7 @@ function createGestures({ state, callbacks }, { signal }) {
547
569
  state.viewer.hideUi = state.viewer.scaleSize < 1 || state.viewer.scaleSize > border;
548
570
  }
549
571
  oldDistance = dist;
550
- callbacks.onGestureMove();
572
+ scheduleGestureMove();
551
573
  }
552
574
  function moveSwipe(e) {
553
575
  const pos = getPos(e);
@@ -596,6 +618,7 @@ function createGestures({ state, callbacks }, { signal }) {
596
618
  }
597
619
  function endPinch() {
598
620
  pinching = false;
621
+ flushGestureMove();
599
622
  const item = currentItem(state);
600
623
  if (!item) {
601
624
  return;
@@ -632,9 +655,11 @@ function createGestures({ state, callbacks }, { signal }) {
632
655
  const items = currentItems(state) ?? [];
633
656
  if (moveDir === "horizontal") {
634
657
  let result = "stay";
635
- if (swipeWidth >= state.options.swipeOffset && state.viewer.currentIndex !== 0) {
658
+ const elapsedMs = Math.max(now - swipeStartTime, 1);
659
+ const isFlick = Math.abs(swipeWidth) >= MIN_FLICK_DISTANCE && Math.abs(swipeWidth) / elapsedMs >= state.options.swipeVelocity;
660
+ if ((swipeWidth >= state.options.swipeOffset || isFlick && swipeWidth > 0) && state.viewer.currentIndex !== 0) {
636
661
  result = "prev";
637
- } else if (swipeWidth <= -state.options.swipeOffset && state.viewer.currentIndex !== items.length - 1) {
662
+ } else if ((swipeWidth <= -state.options.swipeOffset || isFlick && swipeWidth < 0) && state.viewer.currentIndex !== items.length - 1) {
638
663
  result = "next";
639
664
  }
640
665
  callbacks.onSwipeEnd(result);
@@ -725,6 +750,10 @@ function createGestures({ state, callbacks }, { signal }) {
725
750
  }
726
751
  function detach() {
727
752
  clearInterval(interval);
753
+ if (pinchMoveFrame !== null) {
754
+ cancelAnimationFrame(pinchMoveFrame);
755
+ pinchMoveFrame = null;
756
+ }
728
757
  }
729
758
  return { attach, detach };
730
759
  }
@@ -1056,6 +1085,10 @@ function getWindowWidth() {
1056
1085
  return document.documentElement.clientWidth;
1057
1086
  }
1058
1087
  function getWindowHeight() {
1088
+ const visualViewport = window.visualViewport;
1089
+ if (visualViewport) {
1090
+ return visualViewport.height * visualViewport.scale;
1091
+ }
1059
1092
  return document.documentElement.clientHeight;
1060
1093
  }
1061
1094
  function isElementArray(source) {
@@ -1088,6 +1121,12 @@ var SmartPhoto = class {
1088
1121
  this.loadAllFired = /* @__PURE__ */ new Set();
1089
1122
  this.syncedGroupId = null;
1090
1123
  // ---- 内部: window イベント ----
1124
+ this.updateViewportHeight = () => {
1125
+ this.view.refs.dialog.style.setProperty(
1126
+ "--smartphoto-vh",
1127
+ `${getWindowHeight()}px`
1128
+ );
1129
+ };
1091
1130
  this.handleResize = () => {
1092
1131
  if (!currentItems(this.state)) {
1093
1132
  return;
@@ -1167,6 +1206,18 @@ var SmartPhoto = class {
1167
1206
  this.openPhoto(restored, null);
1168
1207
  }
1169
1208
  }
1209
+ this.updateViewportHeight();
1210
+ if (window.visualViewport) {
1211
+ window.visualViewport.addEventListener(
1212
+ "resize",
1213
+ this.updateViewportHeight,
1214
+ { signal: this.abortController.signal }
1215
+ );
1216
+ } else {
1217
+ window.addEventListener("resize", this.updateViewportHeight, {
1218
+ signal: this.abortController.signal
1219
+ });
1220
+ }
1170
1221
  if (!this.isSmartPhoneFlag) {
1171
1222
  window.addEventListener("resize", this.handleResize, {
1172
1223
  signal: this.abortController.signal
@@ -1468,6 +1519,10 @@ var SmartPhoto = class {
1468
1519
  return typeof document.startViewTransition === "function";
1469
1520
  }
1470
1521
  openPhotoWithViewTransition(trigger) {
1522
+ document.documentElement.style.setProperty(
1523
+ "--smartphoto-animation-speed",
1524
+ `${this.state.options.animationSpeed}ms`
1525
+ );
1471
1526
  const transitionName = "smartphoto-hero";
1472
1527
  const thumbImg = trigger?.querySelector("img") ?? null;
1473
1528
  if (thumbImg) {
@@ -59,6 +59,7 @@ export default class SmartPhoto {
59
59
  private loadNeighborItems;
60
60
  private slideList;
61
61
  private scheduleTimeout;
62
+ private updateViewportHeight;
62
63
  private handleResize;
63
64
  private handleKeydown;
64
65
  private handleOrientationChange;
@@ -43,6 +43,7 @@ export interface SmartPhotoOptions {
43
43
  swipeTopToClose: boolean;
44
44
  swipeBottomToClose: boolean;
45
45
  swipeOffset: number;
46
+ swipeVelocity: number;
46
47
  headerHeight: number;
47
48
  footerHeight: number;
48
49
  forceInterval: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartphoto",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "description": "smartphoto",
5
5
  "homepage": "https://developer.a-blogcms.jp",
6
6
  "main": "./lib/smartphoto.js",
@@ -31,17 +31,17 @@
31
31
  "lint": "biome check --write ./src",
32
32
  "lint:ci": "biome ci ./src",
33
33
  "typecheck": "tsc --noEmit",
34
- "build": "npm run build:js && npm run build:types && npm run build:sass",
34
+ "build": "npm run build:js && npm run build:types && npm run build:css",
35
35
  "build:js": "tsup",
36
36
  "build:types": "tsc -p tsconfig.build.json",
37
- "build:sass": "npm run clean:css && npm run sass && npm run sass:min",
38
- "clean:css": "rm -rf ./css/*",
39
- "sass": "sass ./scss/smartphoto.scss ./css/smartphoto.css --style expanded",
40
- "sass:min": "sass ./scss/smartphoto.scss ./css/smartphoto.min.css --style compressed --no-source-map",
37
+ "build:css": "npm run clean:css && npm run css:copy && npm run css:min",
38
+ "clean:css": "rm -rf ./css && mkdir -p ./css",
39
+ "css:copy": "cp ./styles/smartphoto.css ./css/smartphoto.css",
40
+ "css:min": "postcss ./styles/smartphoto.css -o ./css/smartphoto.min.css",
41
41
  "dev": "vite",
42
42
  "preview": "vite preview",
43
43
  "prepare": "husky",
44
- "prepack": "npm run lint:ci && npm run typecheck && npm run test:coverage && npm run build",
44
+ "prepack": "npm run lint:ci && npm run typecheck && npm test && npm run build",
45
45
  "release:patch": "npm version patch && git push --follow-tags",
46
46
  "release:minor": "npm version minor && git push --follow-tags",
47
47
  "release:major": "npm version major && git push --follow-tags"
@@ -64,10 +64,12 @@
64
64
  "@testing-library/jest-dom": "^7.0.0",
65
65
  "@types/node": "^24.13.3",
66
66
  "@vitest/coverage-v8": "^4.1.10",
67
+ "cssnano": "^8.0.4",
67
68
  "husky": "^9.1.7",
68
69
  "jsdom": "^30.0.1",
69
70
  "lint-staged": "^17.3.0",
70
- "sass": "^1.102.0",
71
+ "postcss": "^8.5.26",
72
+ "postcss-cli": "^11.0.1",
71
73
  "tsup": "^8.5.1",
72
74
  "typescript": "^7.0.2",
73
75
  "vite": "^8.2.0",
@@ -1 +0,0 @@
1
- {"version":3,"sourceRoot":"","sources":["../scss/smartphoto.scss"],"names":[],"mappings":"AAKA;EACE;IACE;;EAGF;IACE;;;AAIJ;EACE;IACE;;EAGF;IACE;;;AAIJ;EACE;IACE;;EAGF;IACE;;;AAIJ;EACE;IACE;IACA;;EAEF;IACE;IACA;;EAEF;IACE;IACA;;;AAUJ;EACE;IACE;;EAGF;IACE;;;AAIJ;EACE;IACE;;EAEF;IACE;;;AAIJ;EACE;;;AAIF;EAEE;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAKA;EAKA;EACA;EACA;EACA;EACA;EACA;EAKA,YACE;;AAIF;EACE;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EAEA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;;AAIJ;EACE;EACA;;;AAGF;EAGE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AACA;EACE;;;AAIJ;EACE;EACA;EACA;;AACA;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EAGE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;;AAKN;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA","file":"smartphoto.css"}