react-3d-carousel-fullcontrol 1.0.0 → 1.1.0
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/package.json +1 -1
- package/src/Carousel.jsx +41 -28
package/package.json
CHANGED
package/src/Carousel.jsx
CHANGED
|
@@ -174,13 +174,13 @@ const CarouselCard = memo(function CarouselCard({
|
|
|
174
174
|
style={{
|
|
175
175
|
width: "100%",
|
|
176
176
|
height: "100%",
|
|
177
|
-
borderRadius: "
|
|
177
|
+
borderRadius: "0px",
|
|
178
178
|
overflow: "hidden",
|
|
179
179
|
position: "relative",
|
|
180
180
|
cursor: "pointer",
|
|
181
181
|
willChange: "transform",
|
|
182
182
|
boxShadow: hovered
|
|
183
|
-
? `0 20px 40px rgba(0,0,0,0.8)`
|
|
183
|
+
? `0 0 30px rgba(160,120,255,0.8), 0 0 60px rgba(100,80,220,0.6), 0 0 100px rgba(60,40,180,0.4), 0 20px 40px rgba(0,0,0,0.8)`
|
|
184
184
|
: `0 10px 20px rgba(0,0,0,0.5)`,
|
|
185
185
|
transition: "box-shadow 0.4s ease",
|
|
186
186
|
}}
|
|
@@ -194,7 +194,7 @@ const CarouselCard = memo(function CarouselCard({
|
|
|
194
194
|
height: "100%",
|
|
195
195
|
objectFit: "cover",
|
|
196
196
|
display: "block",
|
|
197
|
-
borderRadius: "
|
|
197
|
+
borderRadius: "0px",
|
|
198
198
|
filter: hovered
|
|
199
199
|
? "brightness(1.2) saturate(1.3) contrast(1.1)"
|
|
200
200
|
: `brightness(${0.65 + frontness * 0.3}) saturate(1.05)`,
|
|
@@ -261,7 +261,7 @@ const CarouselCard = memo(function CarouselCard({
|
|
|
261
261
|
right: "5%",
|
|
262
262
|
height: "80px",
|
|
263
263
|
background: `url(${image.src}) center/cover no-repeat`,
|
|
264
|
-
borderRadius: "
|
|
264
|
+
borderRadius: "0px",
|
|
265
265
|
transform: "scaleY(-1)",
|
|
266
266
|
opacity: 0.12 + frontness * 0.08,
|
|
267
267
|
maskImage: "linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, transparent 100%)",
|
|
@@ -277,14 +277,15 @@ const CarouselCard = memo(function CarouselCard({
|
|
|
277
277
|
// ─── Main reusable carousel component ────────────────────────────────────────
|
|
278
278
|
|
|
279
279
|
/**
|
|
280
|
-
* 3D Carousel with full axis
|
|
280
|
+
* 3D Carousel with cylinder-style rotation and full axis control
|
|
281
|
+
* Cards arranged in a circle and the entire cylinder rotates
|
|
281
282
|
*
|
|
282
283
|
* @param {Object} props
|
|
283
284
|
* @param {Array<{src: string, label?: string}>} props.images
|
|
284
285
|
* @param {Object} [props.defaultRotation] - Default rotation { x: -20, y: 10, z: 20 }
|
|
285
286
|
* @param {boolean} [props.controlled=false] - Return to default position on release
|
|
286
287
|
* @param {function} [props.onRotationChange] - Callback with current rotation
|
|
287
|
-
* @param {number} [props.autoRotateSpeed=0.3] - Auto rotation speed
|
|
288
|
+
* @param {number} [props.autoRotateSpeed=0.3] - Auto rotation speed (degrees per frame)
|
|
288
289
|
* @param {Object} [props.autoRotateAxes] - Which axes to auto-rotate { x: false, y: true, z: false }
|
|
289
290
|
* @param {boolean} [props.pauseOnDrag=true] - Pause auto-rotation while dragging
|
|
290
291
|
* @param {number} [props.cardWidth=220]
|
|
@@ -293,6 +294,7 @@ const CarouselCard = memo(function CarouselCard({
|
|
|
293
294
|
* @param {number} [props.perspective=2000]
|
|
294
295
|
* @param {number} [props.sensitivity=0.4] - Mouse drag sensitivity
|
|
295
296
|
* @param {boolean} [props.showDragHint=true]
|
|
297
|
+
* @param {boolean} [props.glowEffect=true] - Enable glow effect on hover
|
|
296
298
|
* @param {string} [props.className=""]
|
|
297
299
|
* @param {Object} [props.style={}]
|
|
298
300
|
*/
|
|
@@ -310,6 +312,7 @@ function Carousel({
|
|
|
310
312
|
perspective = 2000,
|
|
311
313
|
sensitivity = 0.4,
|
|
312
314
|
showDragHint = true,
|
|
315
|
+
glowEffect = true,
|
|
313
316
|
className = "",
|
|
314
317
|
style = {},
|
|
315
318
|
}) {
|
|
@@ -395,9 +398,6 @@ function Carousel({
|
|
|
395
398
|
rotationRef.current.z += (targetZ - rotationRef.current.z) * returnSpeed;
|
|
396
399
|
}
|
|
397
400
|
}
|
|
398
|
-
} else if (pauseOnDrag) {
|
|
399
|
-
// During drag: only apply auto-rotation if pauseOnDrag is false
|
|
400
|
-
// (Currently paused, but momentum is handled in pointer move)
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
setRenderRotation({ ...rotationRef.current });
|
|
@@ -434,13 +434,13 @@ function Carousel({
|
|
|
434
434
|
const dx = clientX - lastMousePos.current.x;
|
|
435
435
|
const dy = clientY - lastMousePos.current.y;
|
|
436
436
|
|
|
437
|
-
// Horizontal drag = Y axis rotation (spin
|
|
437
|
+
// Horizontal drag = Y axis rotation (spin the cylinder)
|
|
438
438
|
rotationRef.current.y += dx * sensitivity;
|
|
439
439
|
|
|
440
|
-
// Vertical drag = X axis rotation (tilt forward/backward)
|
|
440
|
+
// Vertical drag = X axis rotation (tilt the cylinder forward/backward)
|
|
441
441
|
rotationRef.current.x -= dy * sensitivity * 0.6;
|
|
442
442
|
|
|
443
|
-
// Diagonal drag = Z axis rotation (roll/twist)
|
|
443
|
+
// Diagonal drag = Z axis rotation (roll/twist the cylinder)
|
|
444
444
|
if (Math.abs(dx) > 2 && Math.abs(dy) > 2) {
|
|
445
445
|
const diagonalComponent = (dx * dy) / Math.sqrt(dx * dx + dy * dy);
|
|
446
446
|
rotationRef.current.z += diagonalComponent * sensitivity * 0.3;
|
|
@@ -507,7 +507,7 @@ function Carousel({
|
|
|
507
507
|
);
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
-
// Sort cards by depth for proper rendering
|
|
510
|
+
// Sort cards by depth for proper rendering order (back to front)
|
|
511
511
|
const sortedImages = [...images].map((image, i) => {
|
|
512
512
|
const angle = i * angleStep;
|
|
513
513
|
const totalAngle = (renderRotation.y + angle) % 360;
|
|
@@ -537,30 +537,43 @@ function Carousel({
|
|
|
537
537
|
onTouchMove={onTouchMove}
|
|
538
538
|
onTouchEnd={onTouchEnd}
|
|
539
539
|
>
|
|
540
|
-
{/*
|
|
540
|
+
{/* Outer container - tilts the entire cylinder on X and Z axes */}
|
|
541
541
|
<div
|
|
542
542
|
style={{
|
|
543
543
|
position: "absolute",
|
|
544
544
|
top: "50%",
|
|
545
545
|
left: "50%",
|
|
546
546
|
transformStyle: "preserve-3d",
|
|
547
|
-
transform: `translate(-50%, -50%) rotateX(${renderRotation.x}deg)
|
|
547
|
+
transform: `translate(-50%, -50%) rotateX(${renderRotation.x}deg) rotateZ(${renderRotation.z}deg)`,
|
|
548
548
|
willChange: "transform",
|
|
549
549
|
}}
|
|
550
550
|
>
|
|
551
|
-
{
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
551
|
+
{/* Inner container - rotates the cards around Y axis (cylinder spin) */}
|
|
552
|
+
<div
|
|
553
|
+
style={{
|
|
554
|
+
position: "absolute",
|
|
555
|
+
top: "50%",
|
|
556
|
+
left: "50%",
|
|
557
|
+
transformStyle: "preserve-3d",
|
|
558
|
+
transform: `translate(-50%, -50%) rotateY(${renderRotation.y}deg)`,
|
|
559
|
+
willChange: "transform",
|
|
560
|
+
}}
|
|
561
|
+
>
|
|
562
|
+
{/* Render cards in sorted order (back to front) */}
|
|
563
|
+
{sortedImages.map(({ image, index, zDepth }) => (
|
|
564
|
+
<CarouselCard
|
|
565
|
+
key={index}
|
|
566
|
+
image={image}
|
|
567
|
+
index={index}
|
|
568
|
+
totalAngle={renderRotation.y}
|
|
569
|
+
opacity={0.3 + (zDepth + 1) * 0.35}
|
|
570
|
+
angleStep={angleStep}
|
|
571
|
+
radius={radius}
|
|
572
|
+
cardWidth={cardWidth}
|
|
573
|
+
cardHeight={cardHeight}
|
|
574
|
+
/>
|
|
575
|
+
))}
|
|
576
|
+
</div>
|
|
564
577
|
</div>
|
|
565
578
|
|
|
566
579
|
{showDragHint && (
|