slidev-theme-watabegg 1.1.2 → 1.1.3

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.ja.md CHANGED
@@ -52,7 +52,7 @@ transition: fade
52
52
  | cover | 表紙 | グラデーション波 + title/subtitle/author |
53
53
  | two-cols | 2カラム | `::left::` / `::right::` スロット |
54
54
  | image | 背景画像 | `image:` 指定 + `TextBox` で自由配置 |
55
- | image-scroll | 背景画像(パンとズーム) | `image:` 指定 + 縦長背景 + `TextBox` |
55
+ | image-scroll | 背景画像(縦スクロール) | `image:` 指定 + 縦長背景 + `TextBox` |
56
56
  | end | 終了スライド | シンプルな終了画面 |
57
57
 
58
58
  ### two-cols 例
@@ -82,31 +82,27 @@ image: /path/to/bg.jpg
82
82
  ---
83
83
  layout: image-scroll
84
84
  image: /path/to/long-bg.jpg
85
+ imageScroll:
86
+ offsetY: -120
85
87
  ---
86
88
  ```
87
89
 
88
90
  ![image-scroll 例](https://raw.githubusercontent.com/watabegg/slidev-theme-watabegg/refs/heads/main/example/7.png)
89
91
 
90
92
  image-scroll オプション(frontmatter `imageScroll`):
91
- - `fit`: `contain | cover | width | height`(既定 `contain`)
92
- - `position`: `center | top | bottom | left | right` もしくは組み合わせ(例: `top left`)
93
- - `offset`: `[x, y]` または `{ x, y }`(px)
93
+ - `offsetY`: 画像の中心からの初期スクロール量(px)
94
94
 
95
95
  操作:
96
- - Wheel: 縦パン
97
- - Shift + Wheel: 横パン
98
- - Alt + Wheel: 10% ずつ拡大縮小
99
- - タッチ: ドラッグでパン、ピンチでズーム
96
+ - Wheel / トラックパッド: 縦スクロール
97
+ - タッチ: 縦スクロール
100
98
 
101
- 初期フィット/位置の例:
99
+ 初期位置の例:
102
100
  ```markdown
103
101
  ---
104
102
  layout: image-scroll
105
103
  image: /path/to/long-bg.jpg
106
104
  imageScroll:
107
- fit: width
108
- position: center
109
- offset: [0, -120]
105
+ offsetY: 180
110
106
  ---
111
107
  ```
112
108
 
package/README.md CHANGED
@@ -56,7 +56,7 @@ Below: `color: blue` example.
56
56
  | cover | Title slide | Gradient wave + title/subtitle/author |
57
57
  | two-cols | Two columns | Slots: `::left::` / `::right::` |
58
58
  | image | Background image | Use `image:` + absolutely placed `TextBox` |
59
- | image-scroll | Background image (pan & zoom) | Use `image:` with tall background + `TextBox` |
59
+ | image-scroll | Background image (vertical scroll) | Use `image:` with tall background + `TextBox` |
60
60
  | end | Ending slide | Simple finish screen |
61
61
 
62
62
  ### two-cols Example
@@ -86,31 +86,27 @@ image: /path/to/bg.jpg
86
86
  ---
87
87
  layout: image-scroll
88
88
  image: /path/to/long-bg.jpg
89
+ imageScroll:
90
+ offsetY: -120
89
91
  ---
90
92
  ```
91
93
 
92
94
  ![image-scroll Example](https://raw.githubusercontent.com/watabegg/slidev-theme-watabegg/refs/heads/main/example/7.png)
93
95
 
94
96
  image-scroll options (frontmatter `imageScroll`):
95
- - `fit`: `contain | cover | width | height` (default `contain`)
96
- - `position`: `center | top | bottom | left | right` or combined (e.g. `top left`)
97
- - `offset`: `[x, y]` or `{ x, y }` in px
97
+ - `offsetY`: initial scroll offset from image center in px
98
98
 
99
99
  Controls:
100
- - Wheel: pan vertically
101
- - Shift + Wheel: pan horizontally
102
- - Alt + Wheel: zoom in/out by 10%
103
- - Touch: drag to pan, pinch to zoom
100
+ - Wheel / trackpad: vertical scroll
101
+ - Touch: vertical scroll
104
102
 
105
- Example with initial fit/position:
103
+ Example with initial position:
106
104
  ```markdown
107
105
  ---
108
106
  layout: image-scroll
109
107
  image: /path/to/long-bg.jpg
110
108
  imageScroll:
111
- fit: width
112
- position: center
113
- offset: [0, -120]
109
+ offsetY: 180
114
110
  ---
115
111
  ```
116
112
 
package/example.md CHANGED
@@ -140,15 +140,25 @@ image: 'https://images.unsplash.com/photo-1501594907352-04cda38ebc29?auto=format
140
140
  layout: image-scroll
141
141
  image: 'https://images.unsplash.com/photo-1501785888041-af3ef285b470?auto=format&fit=crop&w=2400&q=80'
142
142
  imageScroll:
143
- fit: width
144
- position: center
143
+ offsetY: -120
145
144
  ---
146
145
 
147
- - マウスホイール: 拡大
148
- - Shift + ホイール: 縮小
149
- - 左クリックホールド + ドラッグ: 移動
146
+ ## image-scroll: 縦長画像を「横幅フィット+縦スクロール」で表示
150
147
 
151
- 画像を全画面で扱いつつ、ズーム・パンを試せます。
148
+ - 画像はスライド幅に合わせて表示
149
+ - 縦方向はホイール / タッチでスクロール
150
+ - `offsetY` で初期位置を調整(中心からのpx)
151
+
152
+ ここでは `offsetY: -120` で少し上寄せしています。
153
+
154
+ ---
155
+ layout: image-scroll
156
+ image: 'https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=2400&q=80'
157
+ ---
158
+
159
+ ## offsetY の例(中心から上へ 300px)
160
+
161
+ 背景の見せたい位置が決まっているときに便利です。
152
162
 
153
163
  ---
154
164
 
@@ -2,24 +2,23 @@
2
2
  <div
3
3
  class="slidev-layout image-scroll"
4
4
  ref="wrapperRef"
5
- @wheel="onWheel"
5
+ @wheel.prevent="onWheel"
6
6
  @pointerdown="onPointerDown"
7
7
  @pointermove="onPointerMove"
8
8
  @pointerup="onPointerUp"
9
9
  @pointercancel="onPointerUp"
10
10
  >
11
- <div class="image-layer" :class="{ 'is-panning': isPanning }">
12
- <div class="image-translate" :style="translateStyle">
13
- <img
14
- v-if="imageSrc"
15
- ref="imageRef"
16
- :src="imageSrc"
17
- alt="Scrollable background"
18
- :style="imageStyle"
19
- draggable="false"
20
- @load="onImageLoad"
21
- />
22
- </div>
11
+ <div class="image-scroll__scroller" ref="scrollerRef">
12
+ <img
13
+ v-if="imageSrc"
14
+ ref="imageRef"
15
+ :src="imageSrc"
16
+ alt="Scrollable background"
17
+ class="image-scroll__image"
18
+ :style="imageStyle"
19
+ draggable="false"
20
+ @load="onImageLoad"
21
+ />
23
22
  </div>
24
23
  <div class="content">
25
24
  <slot />
@@ -28,7 +27,7 @@
28
27
  </template>
29
28
 
30
29
  <script setup lang="ts">
31
- import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
30
+ import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
32
31
  import { useSlideContext } from '@slidev/client'
33
32
 
34
33
  const { $frontmatter } = useSlideContext()
@@ -36,15 +35,10 @@ const { $frontmatter } = useSlideContext()
36
35
  const imageSrc = computed(() => $frontmatter.image)
37
36
 
38
37
  const wrapperRef = ref<HTMLElement | null>(null)
38
+ const scrollerRef = ref<HTMLElement | null>(null)
39
39
  const imageRef = ref<HTMLImageElement | null>(null)
40
40
 
41
- type FitMode = 'contain' | 'cover' | 'width' | 'height'
42
-
43
41
  type ImageScrollFrontmatter = {
44
- fit?: FitMode | string
45
- position?: string
46
- offset?: [number, number] | { x?: number; y?: number }
47
- offsetX?: number
48
42
  offsetY?: number
49
43
  }
50
44
 
@@ -54,266 +48,100 @@ const imageScrollOptions = computed<ImageScrollFrontmatter>(() => {
54
48
  return {}
55
49
  })
56
50
 
57
- const fitMode = computed<FitMode>(() => {
58
- const value = imageScrollOptions.value.fit
59
- if (!value) return 'contain'
60
- const normalized = String(value).toLowerCase()
61
- if (normalized === 'cover') return 'cover'
62
- if (normalized === 'width' || normalized === 'fit-width') return 'width'
63
- if (normalized === 'height' || normalized === 'fit-height') return 'height'
64
- return 'contain'
65
- })
66
-
67
- const positionMode = computed(() => imageScrollOptions.value.position ?? 'center')
68
-
69
- const offsetMode = computed(() => {
70
- const raw = imageScrollOptions.value
71
- const offset = raw.offset
72
- let x = Number(raw.offsetX ?? 0)
73
- let y = Number(raw.offsetY ?? 0)
74
- if (Array.isArray(offset)) {
75
- x = Number(offset[0] ?? x)
76
- y = Number(offset[1] ?? y)
77
- } else if (offset && typeof offset === 'object') {
78
- x = Number((offset as { x?: number }).x ?? x)
79
- y = Number((offset as { y?: number }).y ?? y)
80
- }
81
- return { x, y }
51
+ const offsetY = computed(() => {
52
+ const raw = imageScrollOptions.value.offsetY ?? 0
53
+ const value = Number(raw)
54
+ return Number.isFinite(value) ? value : 0
82
55
  })
83
56
 
84
- const baseTransform = reactive({ x: 0, y: 0, scale: 1 })
85
- const userTransform = reactive({ x: 0, y: 0, scale: 1 })
86
-
87
57
  const imageSize = reactive({ width: 0, height: 0 })
88
58
  const wrapperSize = reactive({ width: 0, height: 0 })
89
59
 
90
- const isPanning = ref(false)
91
- const lastPointer = reactive({ x: 0, y: 0 })
92
- const activePointers = new Map<number, { x: number; y: number }>()
93
- const pinchState = reactive({
94
- active: false,
95
- startDistance: 0,
96
- startUserScale: 1,
97
- anchorImagePoint: { x: 0, y: 0 },
98
- })
99
-
100
- const MIN_SCALE = 0.4
101
- const MAX_SCALE = 4
102
- const ZOOM_STEP = 0.1
103
-
104
60
  const clamp = (val: number, min: number, max: number) => Math.min(max, Math.max(min, val))
105
61
 
106
- const resetTransform = () => {
107
- baseTransform.scale = 1
108
- baseTransform.x = 0
109
- baseTransform.y = 0
110
- userTransform.scale = 1
111
- userTransform.x = 0
112
- userTransform.y = 0
113
- }
114
-
115
- const panBy = (dx: number, dy: number) => {
116
- userTransform.x += dx
117
- userTransform.y += dy
118
- }
119
-
120
- const getWrapperPoint = (event: { clientX: number; clientY: number }) => {
121
- const rect = wrapperRef.value?.getBoundingClientRect()
122
- if (!rect) return { x: 0, y: 0 }
123
- return { x: event.clientX - rect.left, y: event.clientY - rect.top }
124
- }
125
-
126
- const getTotalTransform = () => ({
127
- x: baseTransform.x + userTransform.x,
128
- y: baseTransform.y + userTransform.y,
129
- scale: baseTransform.scale * userTransform.scale,
130
- })
131
-
132
- const getImagePoint = (anchor: { x: number; y: number }, total: { x: number; y: number; scale: number }) => ({
133
- x: (anchor.x - total.x) / total.scale,
134
- y: (anchor.y - total.y) / total.scale,
135
- })
136
-
137
- const applyScaleAtPoint = (nextUserScale: number, anchor: { x: number; y: number }, anchorImagePoint?: { x: number; y: number }) => {
138
- const currentTotal = getTotalTransform()
139
- const imagePoint = anchorImagePoint ?? getImagePoint(anchor, currentTotal)
140
- const nextTotalScale = baseTransform.scale * nextUserScale
141
- const nextTotalX = anchor.x - imagePoint.x * nextTotalScale
142
- const nextTotalY = anchor.y - imagePoint.y * nextTotalScale
143
- userTransform.scale = nextUserScale
144
- userTransform.x = nextTotalX - baseTransform.x
145
- userTransform.y = nextTotalY - baseTransform.y
146
- }
62
+ const isTouching = ref(false)
63
+ const lastTouchY = ref(0)
147
64
 
148
65
  const normalizeWheel = (event: WheelEvent) => {
149
- let { deltaX, deltaY } = event
66
+ let { deltaY } = event
150
67
  if (event.deltaMode === 1) {
151
- deltaX *= 16
152
68
  deltaY *= 16
153
69
  } else if (event.deltaMode === 2) {
154
- deltaX *= wrapperSize.height || 1
155
70
  deltaY *= wrapperSize.height || 1
156
71
  }
157
- return { deltaX, deltaY }
72
+ return deltaY
158
73
  }
159
74
 
160
75
  const onWheel = (event: WheelEvent) => {
161
- if (!imageSrc.value) return
162
-
163
- event.preventDefault()
164
-
165
- if (event.altKey) {
166
- const anchor = getWrapperPoint(event)
167
- const zoomFactor = event.deltaY < 0 ? 1 + ZOOM_STEP : 1 - ZOOM_STEP
168
- const nextScale = clamp(userTransform.scale * zoomFactor, MIN_SCALE, MAX_SCALE)
169
- if (nextScale === userTransform.scale) return
170
- applyScaleAtPoint(nextScale, anchor)
171
- return
172
- }
173
-
174
- const { deltaX, deltaY } = normalizeWheel(event)
175
- if (event.shiftKey) {
176
- panBy(-deltaY, 0)
177
- } else {
178
- panBy(0, -deltaY)
179
- }
76
+ const scroller = scrollerRef.value
77
+ if (!scroller || !imageSrc.value) return
78
+ scroller.scrollTop += normalizeWheel(event)
180
79
  }
181
80
 
182
81
  let resizeObserver: ResizeObserver | null = null
183
82
 
184
83
  const onPointerDown = (event: PointerEvent) => {
185
84
  if (!imageSrc.value) return
186
- if (event.pointerType === 'mouse' && event.button !== 0) return
85
+ if (event.pointerType !== 'touch') return
187
86
  wrapperRef.value?.setPointerCapture(event.pointerId)
188
- const point = getWrapperPoint(event)
189
- activePointers.set(event.pointerId, point)
190
- if (activePointers.size === 1) {
191
- isPanning.value = true
192
- lastPointer.x = point.x
193
- lastPointer.y = point.y
194
- }
195
- if (activePointers.size === 2) {
196
- isPanning.value = false
197
- const [first, second] = Array.from(activePointers.values())
198
- const center = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 }
199
- const distance = Math.hypot(first.x - second.x, first.y - second.y)
200
- const total = getTotalTransform()
201
- pinchState.active = true
202
- pinchState.startDistance = distance
203
- pinchState.startUserScale = userTransform.scale
204
- pinchState.anchorImagePoint = getImagePoint(center, total)
205
- }
87
+ isTouching.value = true
88
+ lastTouchY.value = event.clientY
206
89
  }
207
90
 
208
91
  const onPointerMove = (event: PointerEvent) => {
209
- if (!activePointers.has(event.pointerId)) return
210
- const point = getWrapperPoint(event)
211
- activePointers.set(event.pointerId, point)
212
-
213
- if (activePointers.size === 1 && !pinchState.active) {
214
- const dx = point.x - lastPointer.x
215
- const dy = point.y - lastPointer.y
216
- panBy(dx, dy)
217
- lastPointer.x = point.x
218
- lastPointer.y = point.y
219
- event.preventDefault()
220
- return
221
- }
222
-
223
- if (activePointers.size >= 2 && pinchState.active) {
224
- const [first, second] = Array.from(activePointers.values())
225
- const center = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 }
226
- const distance = Math.hypot(first.x - second.x, first.y - second.y)
227
- if (pinchState.startDistance > 0) {
228
- const factor = distance / pinchState.startDistance
229
- const nextScale = clamp(pinchState.startUserScale * factor, MIN_SCALE, MAX_SCALE)
230
- applyScaleAtPoint(nextScale, center, pinchState.anchorImagePoint)
231
- event.preventDefault()
232
- }
233
- }
92
+ if (!isTouching.value || event.pointerType !== 'touch') return
93
+ const scroller = scrollerRef.value
94
+ if (!scroller) return
95
+ const deltaY = event.clientY - lastTouchY.value
96
+ scroller.scrollTop -= deltaY
97
+ lastTouchY.value = event.clientY
98
+ event.preventDefault()
234
99
  }
235
100
 
236
101
  const onPointerUp = (event: PointerEvent) => {
237
- if (activePointers.has(event.pointerId)) {
238
- activePointers.delete(event.pointerId)
239
- }
240
- if (activePointers.size < 2) {
241
- pinchState.active = false
242
- }
243
- if (activePointers.size === 1) {
244
- const [remaining] = Array.from(activePointers.values())
245
- lastPointer.x = remaining.x
246
- lastPointer.y = remaining.y
247
- isPanning.value = true
248
- } else {
249
- isPanning.value = false
250
- }
102
+ if (event.pointerType !== 'touch') return
103
+ isTouching.value = false
104
+ wrapperRef.value?.releasePointerCapture(event.pointerId)
251
105
  }
252
106
 
253
- const computeBaseTransform = () => {
254
- if (!imageSrc.value) return
255
- if (!imageSize.width || !imageSize.height) return
256
- if (!wrapperSize.width || !wrapperSize.height) return
257
-
258
- const scaleX = wrapperSize.width / imageSize.width
259
- const scaleY = wrapperSize.height / imageSize.height
260
- let scale = scaleX
261
- switch (fitMode.value) {
262
- case 'cover':
263
- scale = Math.max(scaleX, scaleY)
264
- break
265
- case 'height':
266
- scale = scaleY
267
- break
268
- case 'width':
269
- scale = scaleX
270
- break
271
- default:
272
- scale = Math.min(scaleX, scaleY)
273
- }
274
-
275
- const scaledWidth = imageSize.width * scale
276
- const scaledHeight = imageSize.height * scale
277
- const position = String(positionMode.value).toLowerCase()
278
- const parts = position.split(/\s+/).filter(Boolean)
279
- let xAlign: 'left' | 'center' | 'right' = 'center'
280
- let yAlign: 'top' | 'center' | 'bottom' = 'center'
107
+ const scaledHeight = computed(() => {
108
+ if (!imageSize.width || !imageSize.height || !wrapperSize.width) return 0
109
+ return (imageSize.height * wrapperSize.width) / imageSize.width
110
+ })
281
111
 
282
- for (const part of parts) {
283
- if (part === 'left' || part === 'center' || part === 'right') xAlign = part
284
- if (part === 'top' || part === 'center' || part === 'bottom') yAlign = part
285
- }
286
- if (parts.length === 1) {
287
- if (parts[0] === 'top' || parts[0] === 'bottom') {
288
- yAlign = parts[0] as typeof yAlign
289
- xAlign = 'center'
290
- }
291
- if (parts[0] === 'left' || parts[0] === 'right') {
292
- xAlign = parts[0] as typeof xAlign
293
- yAlign = 'center'
294
- }
295
- }
112
+ const getRenderedImageHeight = () => {
113
+ const image = imageRef.value
114
+ if (!image) return 0
115
+ const rect = image.getBoundingClientRect()
116
+ return rect.height
117
+ }
296
118
 
297
- const alignAxis = (container: number, content: number, align: 'left' | 'center' | 'right' | 'top' | 'bottom') => {
298
- if (align === 'left' || align === 'top') return 0
299
- if (align === 'right' || align === 'bottom') return container - content
300
- return (container - content) / 2
301
- }
119
+ const applyDefaultScroll = () => {
120
+ const scroller = scrollerRef.value
121
+ if (!scroller) return
122
+ const renderedHeight = getRenderedImageHeight()
123
+ const totalHeight = renderedHeight || scaledHeight.value
124
+ if (!totalHeight) return
125
+ const viewportHeight = scroller.clientHeight || wrapperSize.height
126
+ const maxScroll = Math.max(0, totalHeight - viewportHeight)
127
+ const desired = maxScroll / 2 + offsetY.value
128
+ scroller.scrollTop = clamp(desired, 0, maxScroll)
129
+ }
302
130
 
303
- const offset = offsetMode.value
304
- baseTransform.scale = scale
305
- baseTransform.x = alignAxis(wrapperSize.width, scaledWidth, xAlign) + offset.x
306
- baseTransform.y = alignAxis(wrapperSize.height, scaledHeight, yAlign) + offset.y
307
- userTransform.scale = 1
308
- userTransform.x = 0
309
- userTransform.y = 0
131
+ const scheduleApplyDefaultScroll = () => {
132
+ if (!imageSrc.value) return
133
+ nextTick(() => {
134
+ requestAnimationFrame(() => {
135
+ applyDefaultScroll()
136
+ })
137
+ })
310
138
  }
311
139
 
312
140
  const onImageLoad = (event: Event) => {
313
141
  const target = event.target as HTMLImageElement
314
142
  imageSize.width = target.naturalWidth
315
143
  imageSize.height = target.naturalHeight
316
- computeBaseTransform()
144
+ scheduleApplyDefaultScroll()
317
145
  }
318
146
 
319
147
  const updateWrapperSize = () => {
@@ -323,28 +151,24 @@ const updateWrapperSize = () => {
323
151
  wrapperSize.height = rect.height
324
152
  }
325
153
 
326
- const imageStyle = computed(() => ({
327
- width: imageSize.width ? `${imageSize.width}px` : 'auto',
328
- height: imageSize.height ? `${imageSize.height}px` : 'auto',
329
- transform: `scale(${baseTransform.scale * userTransform.scale})`,
330
- transformOrigin: '0 0',
331
- }))
332
-
333
- const translateStyle = computed(() => ({
334
- transform: `translate3d(${baseTransform.x + userTransform.x}px, ${baseTransform.y + userTransform.y}px, 0)`,
335
- }))
154
+ const imageStyle = computed(() => {
155
+ if (!imageSize.width || !imageSize.height) return {}
156
+ return {
157
+ aspectRatio: `${imageSize.width} / ${imageSize.height}`,
158
+ }
159
+ })
336
160
 
337
161
  onMounted(() => {
338
162
  updateWrapperSize()
339
163
  if (imageRef.value?.complete && imageRef.value.naturalWidth) {
340
164
  imageSize.width = imageRef.value.naturalWidth
341
165
  imageSize.height = imageRef.value.naturalHeight
342
- computeBaseTransform()
343
166
  }
167
+ scheduleApplyDefaultScroll()
344
168
  if (wrapperRef.value) {
345
169
  resizeObserver = new ResizeObserver(() => {
346
170
  updateWrapperSize()
347
- computeBaseTransform()
171
+ scheduleApplyDefaultScroll()
348
172
  })
349
173
  resizeObserver.observe(wrapperRef.value)
350
174
  }
@@ -353,17 +177,16 @@ onMounted(() => {
353
177
  onBeforeUnmount(() => {
354
178
  resizeObserver?.disconnect()
355
179
  resizeObserver = null
356
- activePointers.clear()
357
180
  })
358
181
 
359
182
  watch(imageSrc, () => {
360
183
  imageSize.width = 0
361
184
  imageSize.height = 0
362
- resetTransform()
185
+ if (scrollerRef.value) scrollerRef.value.scrollTop = 0
363
186
  })
364
187
 
365
- watch([fitMode, positionMode, offsetMode], () => {
366
- computeBaseTransform()
188
+ watch([scaledHeight, offsetY], () => {
189
+ scheduleApplyDefaultScroll()
367
190
  })
368
191
  </script>
369
192
 
@@ -376,32 +199,21 @@ watch([fitMode, positionMode, offsetMode], () => {
376
199
  touch-action: none;
377
200
  }
378
201
 
379
- .image-layer {
202
+ .image-scroll__scroller {
380
203
  position: absolute;
381
204
  inset: 0;
382
- overflow: hidden;
383
- cursor: grab;
384
- user-select: none;
385
- }
386
-
387
- .image-layer.is-panning {
388
- cursor: grabbing;
389
- }
390
-
391
- .image-translate {
392
- position: absolute;
393
- top: 0;
394
- left: 0;
395
- will-change: transform;
205
+ overflow-y: auto;
206
+ overflow-x: hidden;
207
+ scrollbar-gutter: stable;
208
+ overscroll-behavior: contain;
396
209
  }
397
210
 
398
- .image-layer img {
211
+ .image-scroll__image {
399
212
  display: block;
213
+ width: 100%;
400
214
  max-width: none;
401
- max-height: none;
402
215
  pointer-events: none;
403
216
  user-select: none;
404
- will-change: transform;
405
217
  }
406
218
 
407
219
  .content {
@@ -409,5 +221,6 @@ watch([fitMode, positionMode, offsetMode], () => {
409
221
  z-index: 1;
410
222
  height: 100%;
411
223
  padding: 2rem;
224
+ box-sizing: border-box;
412
225
  }
413
226
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slidev-theme-watabegg",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "type": "module",
5
5
  "author": "watabegg",
6
6
  "description": "Slidev theme for educational purposes, especially teaching.",