UNPKG

@timsctt/threed-cloud

Version:

React module for create a cloud of React Nodes

1 lines 16.1 kB
{"version":3,"sources":["../src/hooks/useCloudContainer.ts","../src/hooks/useCloudElement.ts","../src/components/ThreeDCloud/CloudElement.tsx","#style-inject:#style-inject","../src/components/ThreeDCloud/style.css","../src/assets/Pause.tsx","../src/assets/Play.tsx","../src/components/ThreeDCloud/CloudContainer.tsx"],"names":["React","useEffect","useRef","useState","useCloudContainer","children","size","speed","radius","randomPosition","isPausable","iconOnHover","mouseTracking","elements","numberElements","ref","pause","setPause","mousePosition","setMousePosition","a","b","l","sc","elementRandomPosition","computePosition","index","getRandomIndex","theta","phi","closest","previousValue","currentValue","i","elementsList","element","handlePause","handlePauseByKey","event","updateMousePosition","rect","XClient","YClient","useCloudElement","props","scale","setScale","per","setPer","position","setPosition","style","setStyle","nextPosition","rx1","ry1","rz1","rx2","ry2","rz2","getStyle","alpha","left","top","updateStyleInterval","Fragment","jsx","CloudElement","CloudElement_default","styleInject","css","insertAt","head","jsxs","Pause","Play","CloudContainer","elementIndex"],"mappings":"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","sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { CloudElementProps, CloudContainerProps, Position } from '../types';\n\nexport function useCloudContainer({\n children,\n size = 150,\n speed = 1,\n radius = 200,\n randomPosition = true,\n isPausable = true,\n iconOnHover = false,\n mouseTracking = true,\n}: CloudContainerProps) {\n const elements: Array<JSX.Element> = React.Children.toArray(\n children\n ) as Array<JSX.Element>;\n const numberElements = elements.length;\n const ref = useRef<HTMLDivElement>(null);\n\n const [pause, setPause] = useState<boolean>(false);\n const [mousePosition, setMousePosition] = useState<Position>({\n x: -500,\n y: 0,\n z: 0,\n });\n\n // Direction\n const a =\n -(Math.min(Math.max(-mousePosition.y, -size), size) / radius) * speed;\n const b =\n (Math.min(Math.max(-mousePosition.x, -size), size) / radius) * speed;\n const l = Math.PI / size;\n const sc = [\n Math.sin(a * l),\n Math.cos(a * l),\n Math.sin(b * l),\n Math.cos(b * l),\n ];\n\n const elementRandomPosition: Array<Number> = Array.from(elements.keys());\n\n function computePosition(index: number): Position {\n if (randomPosition)\n index = getRandomIndex(\n Math.floor(Math.random() * (numberElements + 1))\n ).valueOf();\n const theta = Math.acos(-1 + (2 * index + 1) / numberElements);\n const phi = Math.sqrt((numberElements + 1) * Math.PI) * theta;\n\n return {\n x: (size * Math.sin(theta) * Math.cos(phi)) / 2,\n y: (size * Math.sin(theta) * Math.sin(phi)) / 2,\n z: (size * Math.cos(theta)) / 2,\n };\n }\n\n function getRandomIndex(index: number): Number {\n const closest = elementRandomPosition.reduce(\n (previousValue: Number, currentValue: Number): Number =>\n Math.abs(currentValue.valueOf() - index) <\n Math.abs(previousValue.valueOf() - index)\n ? currentValue\n : previousValue\n );\n for (let i = 0; i < elementRandomPosition.length; i++) {\n if (elementRandomPosition[i] === closest) {\n elementRandomPosition.splice(i, 1);\n }\n }\n return closest;\n }\n\n const elementsList: Array<CloudElementProps> = elements.map(\n (element, index) => ({\n children: React.createElement(\n element.type,\n element.props,\n element.props.children\n ),\n depth: radius,\n position: computePosition(index),\n size,\n speed,\n sc,\n pause,\n })\n );\n\n const handlePause = (): void => {\n if (isPausable) {\n setPause(!pause);\n }\n };\n\n const handlePauseByKey = (\n event: React.KeyboardEvent<HTMLDivElement>\n ): void => {\n if (event.code === 'Space') {\n handlePause();\n }\n };\n\n const updateMousePosition = (event: MouseEvent) => {\n if (ref.current) {\n const rect = ref.current.getBoundingClientRect();\n const XClient = (event.clientX - (rect.left + rect.width / 2)) / 5;\n const YClient = (event.clientY - (rect.top + rect.height / 2)) / 5;\n setMousePosition({ x: XClient, y: YClient, z: 0 });\n }\n };\n\n useEffect(() => {\n if (mouseTracking) {\n document.addEventListener('mousemove', updateMousePosition);\n\n return () => {\n document.removeEventListener('mousemove', updateMousePosition);\n };\n }\n return undefined;\n }, [mouseTracking]);\n\n return {\n handlePause,\n pause,\n handlePauseByKey,\n isPausable,\n iconOnHover,\n radius,\n sc,\n elementsList,\n ref,\n updateMousePosition,\n mousePosition,\n };\n}\n","import { useEffect, useState } from 'react';\nimport { CloudElementProps } from '../types/index';\nimport { ItemStyle, Position } from '../types';\n\nexport function useCloudElement(props: CloudElementProps) {\n const [scale, setScale] = useState<number>(1);\n const [per, setPer] = useState<number>(1);\n const [position, setPosition] = useState<Position>(props.position);\n const [style, setStyle] = useState<ItemStyle>({});\n\n function nextPosition() {\n const rx1 = position.x;\n const ry1 = position.y * props.sc[1] + position.z * -props.sc[0];\n const rz1 = position.y * props.sc[0] + position.z * props.sc[1];\n\n const rx2 = rx1 * props.sc[3] + rz1 * props.sc[2];\n const ry2 = ry1;\n const rz2 = rz1 * props.sc[3] - rx1 * props.sc[2];\n\n setPosition({ x: rx2, y: ry2, z: rz2 });\n }\n\n function getStyle(): ItemStyle {\n let alpha = per * per - 0.2;\n alpha = Math.abs(Math.round((alpha > 1 ? 1 : alpha) * 1e3) / 1e3);\n\n const left = position.x.toFixed(2);\n const top = position.y.toFixed(2);\n const transform = `translate3d(calc(-50% + ${left}px), calc(-50% + ${top}px), 0) scale(${scale})`;\n\n return {\n transform,\n filter: `alpha(opacity=${Math.abs(100 * alpha)})`,\n opacity: `${alpha}`,\n };\n }\n\n useEffect(() => {\n if (!props.pause) {\n const updateStyleInterval = setInterval(() => {\n nextPosition();\n setPer((2 * props.depth) / (2 * props.depth + position.z));\n setScale(Math.round(per * 1e3) / 1e3);\n setStyle(getStyle());\n }, 15);\n return () => clearInterval(updateStyleInterval);\n }\n return () => {};\n }, [style, props.pause]);\n\n return {\n style,\n scale,\n };\n}\n","import React from 'react';\nimport { useCloudElement } from '../../hooks/useCloudElement';\nimport { CloudElementProps } from '../../types';\n\nconst CloudElement: React.FunctionComponent<CloudElementProps> = (\n props: CloudElementProps\n) => {\n const { style, scale } = useCloudElement(props);\n\n return (\n <>\n <span\n className=\"three-d-item\"\n style={{\n filter: style.filter,\n height: style.height,\n opacity: style.opacity,\n transform: style.transform,\n width: style.width,\n scale,\n }}\n >\n {props.children}\n </span>\n </>\n );\n};\n\nexport default CloudElement;\n","\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\".threed-cloud-container{display:flex;align-items:center;justify-content:center;min-height:100%;position:relative}.three-d-item{will-change:transform,opacity,filter;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}.tdc-animation-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:50px;opacity:0;transition:.3s}.threed-cloud-container:hover .tdc-animation-icon{opacity:.65}.tdcIsPausable{cursor:pointer}.threed-cloud-container span *{display:flex;justify-content:center;align-items:center}\\n\")","export const Pause = () => (\n <>\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" fill=\"#000\" />\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" fill=\"#000\" />\n </svg>\n </>\n);\n","export const Play = () => (\n <>\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">\n <path d=\"M8 5v14l11-7z\" />\n <path d=\"M0 0h24v24H0z\" fill=\"none\" />\n </svg>\n </>\n);\n","import React from 'react';\nimport { useCloudContainer } from '../../hooks/useCloudContainer';\nimport { CloudContainerProps } from '../../types';\nimport CloudElement from './CloudElement';\n\nimport './style.css';\nimport { Pause } from '../../assets/Pause';\nimport { Play } from '../../assets/Play';\n\nexport const CloudContainer: React.FunctionComponent<CloudContainerProps> = (\n props: CloudContainerProps\n) => {\n const {\n sc,\n radius,\n elementsList,\n handlePause,\n pause,\n handlePauseByKey,\n isPausable,\n iconOnHover,\n ref,\n } = useCloudContainer(props);\n\n return (\n <div\n className={`threed-cloud-container ${isPausable ? 'tdcIsPausable' : ''}`}\n role=\"button\"\n onClick={handlePause}\n onKeyDown={handlePauseByKey}\n tabIndex={0}\n >\n {isPausable && iconOnHover && (\n <span className=\"tdc-animation-icon\">\n {pause ? <Play /> : <Pause />}\n </span>\n )}\n <div\n className={props.className}\n style={{\n width: `${props.size}px`,\n height: `${props.size}px`,\n }}\n ref={ref}\n >\n {elementsList.map((element, elementIndex) => (\n <CloudElement\n key={elementIndex}\n depth={radius}\n sc={sc}\n position={element.position}\n pause={pause}\n >\n {element.children}\n </CloudElement>\n ))}\n </div>\n </div>\n );\n};\n"]}