Skip to content

Commit 260ba1b

Browse files
add resizer component for debug, test with html abstraction
1 parent 5db0c4b commit 260ba1b

File tree

3 files changed

+311
-4
lines changed

3 files changed

+311
-4
lines changed

playground/vue/src/pages/abstractions/DecalDemo.vue

+33-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import { TresCanvas } from '@tresjs/core'
3-
import { Decal, OrbitControls } from '@tresjs/cientos'
3+
import { Decal, Html, OrbitControls } from '@tresjs/cientos'
44
import { TresLeches, useControls } from '@tresjs/leches'
55
import { SRGBColorSpace } from 'three'
66
import '@tresjs/leches/styles'
@@ -18,24 +18,54 @@ const gl = {
1818
antialias: pixelRatio.value < 2,
1919
}
2020
21+
const paragraphRef = ref<HTMLElement | null>(null)
22+
const paragraphRefBis = ref<HTMLElement | null>(null)
23+
const paragraphRefBisBis = ref<HTMLElement | null>(null)
24+
const paragraphRefBisBisBis = ref<HTMLElement | null>(null)
25+
const paragraphRefBisBisBisBis = ref<HTMLElement | null>(null)
26+
const paragraphRefBisBisBisBisBis = ref<HTMLElement | null>(null)
27+
28+
onMounted(() => {
29+
console.log('mounted', paragraphRefBis.value)
30+
})
31+
32+
watch(paragraphRefBis, () => {
33+
console.log('watch', paragraphRefBis.value)
34+
})
35+
2136
useControls({})
2237
</script>
2338

2439
<template>
2540
<TresLeches style="position:absolute; left:initial; right:20px; top:10px;" />
41+
<div id="late-div"></div>
2642

2743
<TresCanvas v-bind="gl" window-size>
2844
<TresPerspectiveCamera :position="[7.5, 5, 7.5]" />
2945
<OrbitControls make-default />
3046

31-
<TresMesh :scale="3">
47+
<!-- <TresMesh :scale="3">
3248
<TresMeshStandardMaterial color="white" />
3349
<TresBoxGeometry :args="[1, 1, 1]" />
3450
3551
<Suspense>
3652
<Decal :data="decalsData" debug :scale="2" :map="['/decal/tres-logo.png', '/decal/vuejs-logo.png', '/decal/twemoji.png', '/decal/tres-logo-rotate.png']" />
3753
</Suspense>
38-
</TresMesh>
54+
</TresMesh> -->
55+
56+
<Html center>
57+
<p ref="paragraphRef">Decal Demo</p>
58+
</Html>
59+
60+
<Html center>
61+
<p ref="paragraphRefBis">Decal Demo</p>
62+
<p ref="paragraphRefBisBis">Decal Demo</p>
63+
64+
<div ref="paragraphRefBisBisBis">
65+
<p ref="paragraphRefBisBisBisBis">Decal Demo</p>
66+
<p ref="paragraphRefBisBisBisBisBis">Decal Demo 2</p>
67+
</div>
68+
</Html>
3969

4070
<!-- <Sphere :args="[1.75, 32, 32]">
4171
<Suspense>
+259
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
<script setup lang="ts">
2+
import { onMounted, reactive, ref, watch } from 'vue'
3+
import { gsap } from 'gsap'
4+
import { Draggable } from 'gsap/Draggable'
5+
import { useElementBounding, useElementSize, useMouse, useWindowSize } from '@vueuse/core'
6+
import type { UseMouseEventExtractor } from '@vueuse/core'
7+
import { Html } from '../../index'
8+
9+
const emit = defineEmits(['updateTransform'])
10+
11+
let dragTransform, dragScaleRotation
12+
13+
gsap.registerPlugin(Draggable)
14+
15+
const container = ref<HTMLElement | null>(null)
16+
const outline = ref<HTMLElement | null>(null)
17+
const wrapper = ref<HTMLElement | null>(null)
18+
const handle = ref<HTMLElement | null>(null)
19+
const isVisible = ref(false)
20+
const resizerIsActive = ref(false)
21+
const isGrabbing = ref(false)
22+
const angle = ref(0)
23+
24+
const { width: windowWidth, height: windowHeight } = useWindowSize()
25+
26+
const { width: wrapperWidth, height: wrapperHeight } = useElementSize(wrapper)
27+
const { width, height, left, top } = useElementBounding(container)
28+
29+
const extractor: UseMouseEventExtractor = (event) => {
30+
if (event instanceof Touch) { return null }
31+
32+
const offsetX = event.clientX
33+
const offsetY = event.clientY
34+
35+
return [offsetX, offsetY]
36+
}
37+
38+
const { x: xWindow, y: yWindow } = useMouse({ type: extractor })
39+
40+
const origSize = reactive({ width: 0, height: 0 })
41+
const len1 = ref(0)
42+
const angle1 = ref(0)
43+
const scale = ref(1)
44+
45+
watch([xWindow, yWindow], () => {
46+
if (isGrabbing.value) { return }
47+
48+
const centerX = left.value
49+
const centerY = top.value
50+
51+
const distX = xWindow.value - centerX
52+
const distY = yWindow.value - centerY
53+
const distance = Math.sqrt(distX * distX + distY * distY)
54+
55+
const radius = Math.min(wrapperWidth.value, wrapperHeight.value) / 2
56+
const distanceNormalized = distance / radius
57+
58+
resizerIsActive.value = distanceNormalized < 1.05 && distanceNormalized > 0.95
59+
60+
const angleRad = Math.atan2(distY, distX)
61+
angle.value = angleRad + Math.PI / 2
62+
})
63+
64+
watch(resizerIsActive, () => {
65+
// if (resizerIsActive.value) {
66+
// dragTransform[0].disable()
67+
// dragScaleRotation[0].enable()
68+
// }
69+
// else {
70+
// dragTransform[0].enable()
71+
// dragScaleRotation[0].disable()
72+
// }
73+
})
74+
75+
origSize.width = wrapperWidth.value
76+
origSize.height = wrapperHeight.value
77+
78+
const origVector = {
79+
x: 0,
80+
y: -origSize.height / 2,
81+
}
82+
83+
len1.value = Math.sqrt(origVector.x * origVector.x + origVector.y * origVector.y)
84+
85+
const v1 = {
86+
x: origVector.x / len1.value,
87+
y: origVector.y / len1.value,
88+
}
89+
90+
angle1.value = Math.atan2(v1.y, v1.x)
91+
92+
onMounted(() => {
93+
console.log(container.value, handle.value, outline.value, wrapper.value)
94+
gsap.set([container.value, handle.value, outline.value, wrapper.value], {
95+
xPercent: -50,
96+
yPercent: -50,
97+
})
98+
99+
gsap.set(outline.value, {
100+
width: origSize.width,
101+
height: origSize.height,
102+
})
103+
104+
gsap.set(handle.value, {
105+
x: origVector.x,
106+
y: origVector.y,
107+
rotation: angle1.value,
108+
})
109+
110+
dragTransform = Draggable.create(container.value, {
111+
onDrag(this: Draggable) {
112+
updateTransformData(scale.value, angle.value, this.x, this.y)
113+
},
114+
})
115+
116+
dragScaleRotation = Draggable.create(handle.value, {
117+
onPress: stopPropagation,
118+
onRelease: () => { isGrabbing.value = false },
119+
onDrag(this: Draggable) {
120+
transformItems(this)
121+
},
122+
})
123+
})
124+
125+
function getCenteredPosition(x: number, y: number) {
126+
const centerX = windowWidth.value / 2
127+
const centerY = windowHeight.value / 2
128+
129+
return {
130+
x: x - centerX,
131+
y: y - centerY,
132+
}
133+
}
134+
135+
function updateTransformData(scaleValue: number, rotationValue: number, posX: number, posY: number) {
136+
emit('updateTransform', {
137+
scale: scaleValue,
138+
rotation: rotationValue,
139+
position: { x: posX, y: posY },
140+
})
141+
}
142+
143+
function transformItems(self: Draggable) {
144+
const v2 = {
145+
x: self.x,
146+
y: self.y,
147+
}
148+
149+
const len2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y)
150+
151+
if (len2) {
152+
v2.x /= len2
153+
v2.y /= len2
154+
}
155+
156+
const angle = Math.atan2(v2.y, v2.x) - angle1.value
157+
158+
scale.value = len2 / len1.value
159+
160+
gsap.set(outline.value, {
161+
width: origSize.width * scale.value,
162+
height: origSize.height * scale.value,
163+
rotation: `${angle}_rad`,
164+
})
165+
166+
gsap.set(wrapper.value, {
167+
scale: scale.value,
168+
rotation: `${angle}_rad`,
169+
})
170+
171+
gsap.set(handle.value, {
172+
// rotation: Math.atan2(-self.y, -self.x) * (180 / Math.PI) + 90,
173+
})
174+
175+
const { x: posX, y: posY } = getCenteredPosition(left.value, top.value)
176+
177+
updateTransformData(scale.value, angle, posX, posY)
178+
}
179+
180+
function stopPropagation(event) {
181+
event.stopPropagation()
182+
183+
isGrabbing.value = true
184+
console.log('start drag')
185+
}
186+
187+
function updatePosition() {
188+
console.log('update position resizer')
189+
// const { x: posX, y: posY } = getCenteredPosition(left.value, top.value)
190+
191+
// updateTransformData(scale.value, angle.value, posX, posY)
192+
}
193+
194+
defineExpose({ updatePosition, instance: container })
195+
</script>
196+
197+
<template>
198+
<Html
199+
center
200+
>
201+
<div ref="container" class="container" :class="{ hide: !isVisible }">
202+
<div ref="wrapper" class="wrapper"></div>
203+
<span ref="outline" class="outline">outline</span>
204+
<span class="dot-center"></span>
205+
<div ref="handle" class="handle">handle</div>
206+
</div>
207+
</Html>
208+
</template>
209+
210+
<style scoped>
211+
.container,
212+
.wrapper,
213+
.outline,
214+
.handle {
215+
position: absolute;
216+
top: 50%;
217+
left: 50%;
218+
}
219+
220+
.container.hide {
221+
/* opacity: 0;
222+
pointer-events: none; */
223+
}
224+
225+
.dot-center {
226+
position: absolute;
227+
top: 50%;
228+
left: 50%;
229+
width: 10px;
230+
height: 10px;
231+
background-color: white;
232+
border-radius: 50%;
233+
transform: translate(-50%, -50%);
234+
}
235+
236+
.wrapper {
237+
width: 450px;
238+
height: 450px;
239+
border-radius: 50%;
240+
/* background-color: rgba(255, 0, 0, 0.513); */
241+
}
242+
243+
.handle {
244+
width: 50px;
245+
height: 12px;
246+
border-radius: 5px;
247+
background-color: white;
248+
transform: translate(-50%, -50%);
249+
box-shadow: 0 0 3px #00000080;
250+
}
251+
252+
.outline {
253+
width: 100%;
254+
height: 100%;
255+
border-radius: 50%;
256+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
257+
outline: 5px dashed white;
258+
}
259+
</style>

src/core/abstractions/Decal/index.vue

+19-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import type { BoxHelper, Group, Intersection } from 'three'
66
import { Color, Euler, MathUtils, Mesh, SRGBColorSpace, Vector3 } from 'three'
77
import { DecalGeometry } from 'three-stdlib'
88
import { useRenderLoop, useTexture, useTresContext } from '@tresjs/core'
9-
import { Box } from '../../index'
9+
import { Box, Html } from '../../index'
1010
import Item from './Item.vue'
11+
import Resizer from './Resizer.vue'
1112
import { parseTextureFilename, updateBoxHelper } from './utils'
1213
import { useControls } from '@tresjs/leches'
1314
import { useClipboard } from '@vueuse/core'
@@ -25,6 +26,7 @@ const props = withDefaults(defineProps<DecalAttributes>(), {
2526
mesh: () => shallowRef(null),
2627
})
2728

29+
const resizerRef = ref(null)
2830
const nodesDecalRefs = ref<Decal[]>([])
2931
const decalItemsRef = ref<DecalElementProps[]>([])
3032
const textureMap = ref<Record<string, EnhancedTexture>>({})
@@ -489,6 +491,8 @@ const printDecal = async () => {
489491

490492
await nextTick()
491493

494+
console.log(point, resizerRef.value)
495+
492496
updateControlsFromDecal(decalDebugScale.value, decalDebugOrientationZ.value)
493497

494498
decalSelected.value.options = computedNodesDecal.value
@@ -499,6 +503,14 @@ const printDecal = async () => {
499503
updateBoxHelper(boxHelperSelectedRef, targetMesh?.instance)
500504
}
501505

506+
watch(resizerRef, (newVal) => {
507+
console.log('resizerRef:', newVal)
508+
if (newVal) {
509+
console.log('updatePosition:', newVal.updatePosition)
510+
console.log('root:', newVal.root)
511+
}
512+
})
513+
502514
const onPointerUp = () => {
503515
if (currentIntersectIsEmpty.value || intersectIsEmpty.value) { return }
504516

@@ -512,6 +524,10 @@ const onPointerUp = () => {
512524
}
513525
}
514526

527+
function onUpdateResizer({ scale, rotation, position }) {
528+
console.log('onUpdateResizer:', position, scale, rotation)
529+
}
530+
515531
watch(() => decalSelected.value.value, async (newVal) => {
516532
if (!boxHelperCurrentRef.value || !boxHelperSelectedRef.value || !currentNodesDecalRefs.value) { return }
517533

@@ -707,6 +723,8 @@ onUnmounted(() => {
707723
<TresLineBasicMaterial :color="meshLineColor" />
708724
</TresLine>
709725

726+
<Resizer ref="resizerRef" @update-transform="onUpdateResizer" />
727+
710728
<Box
711729
v-if="debug"
712730
ref="boxHelperRef"

0 commit comments

Comments
 (0)