| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
Tags
- #쇼핑몰
- 해피CGI
- #홈페이지
- 홈페이지
- 사이트제작
- #이미지
- 솔루션
- happycgi
- #해피CGI
- #jQuery
- 게시판
- 홈페이지제작
- #CSS
- #뉴스
- 해피씨지아이
- #happycgi
- #솔루션
- javascript
- #동영상
- #image
- 웹솔루션
- CSS
- 이미지
- #홈페이지제작
- #웹솔루션
- jquery
- #업종별
- #cgimall
- CGIMALL
- php
- Today
- Total
웹솔루션개발 25년 노하우! 해피CGI의 모든것
[해피CGI][cgimall] Interactive Blackhole 본문
사이트의 시작 버튼 디자인으로 사용하거나 역동적인 디자인이 필요할때 적용해 보면 좋을거 같습니다.
자세한 내용은 데모를 참고하시기 바랍니다.

HTML
|
<div id="blackhole">
<div class="centerHover"><span>ENTER</span></div>
</div>
<script src="script.js"></script>
|
CSS
|
body, html {
height: 100%;
margin: 0;
padding: 0;
}
body {
height: 100%;
background-color: rgba(25,25,25,1);
overflow: hidden;
}
#blackhole {
height: 100%;
width: 100%;
position: relative;
display: flex;
}
.centerHover {
width: 255px;
height: 255px;
background-color: transparent;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
margin-top: -128px;
margin-left: -128px;
z-index: 2;
cursor: pointer;
line-height: 255px;
text-align: center;
transition: all 500ms;
}
.centerHover.open {
opacity: 0;
pointer-events: none;
}
.centerHover:hover span {
color: #DDD;
}
.centerHover:hover span:before {
background-color: #DDD;
}
.centerHover:hover span:after {
background-color: #DDD;
}
.centerHover span {
color: #666;
font-family: serif;
font-size: 18px;
position: relative;
transition: all 500ms;
}
.centerHover span:before {
content: '';
display: inline-block;
height: 1px;
width: 16px;
margin-right: 12px;
margin-bottom: 4px;
background-color: #666;
transition: all 500ms;
}
.centerHover span:after {
content: '';
display: inline-block;
height: 1px;
width: 16px;
margin-left: 12px;
margin-bottom: 4px;
background-color: #666;
transition: all 500ms;
}
canvas {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
margin: auto;
}
#blackhole {
height: 100%;
width: 100%;
position: relative;
display: flex;
}
.centerHover {
width: 255px;
height: 255px;
background-color: transparent;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
margin-top: -128px;
margin-left: -128px;
z-index: 2;
cursor: pointer;
line-height: 255px;
text-align: center;
transition: all 500ms;
}
.centerHover.open {
opacity: 0;
pointer-events: none;
}
.centerHover:hover span {
color: #DDD;
}
.centerHover:hover span:before {
background-color: #DDD;
}
.centerHover:hover span:after {
background-color: #DDD;
}
.centerHover span {
color: #666;
font-family: serif;
font-size: 18px;
position: relative;
transition: all 500ms;
}
.centerHover span:before {
content: '';
display: inline-block;
height: 1px;
width: 16px;
margin-right: 12px;
margin-bottom: 4px;
background-color: #666;
transition: all 500ms;
}
.centerHover span:after {
content: '';
display: inline-block;
height: 1px;
width: 16px;
margin-left: 12px;
margin-bottom: 4px;
background-color: #666;
transition: all 500ms;
}
canvas {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
margin: auto;
}
|
JS
|
function blackhole(element) {
const container = document.querySelector(element);
const h = container.offsetHeight;
const w = container.offsetWidth;
const cw = w;
const ch = h;
const maxorbit = 255; // distance from center
const centery = ch / 2;
const centerx = cw / 2;
const startTime = new Date().getTime();
let currentTime = 0;
const stars = [];
let collapse = false; // if hovered
let expanse = false; // if clicked
let returning = false; // if particles are returning to orbit
// Create canvas
const canvas = document.createElement('canvas');
canvas.width = cw;
canvas.height = ch;
container.appendChild(canvas);
const context = canvas.getContext("2d");
context.globalCompositeOperation = "multiply";
function setDPI(canvas, dpi) {
// Set up CSS size if it's not set up already
if (!canvas.style.width)
canvas.style.width = canvas.width + 'px';
if (!canvas.style.height)
canvas.style.height = canvas.height + 'px';
const scaleFactor = dpi / 96;
canvas.width = Math.ceil(canvas.width * scaleFactor);
canvas.height = Math.ceil(canvas.height * scaleFactor);
const ctx = canvas.getContext('2d');
ctx.scale(scaleFactor, scaleFactor);
}
function rotate(cx, cy, x, y, angle) {
const radians = angle;
const cos = Math.cos(radians);
const sin = Math.sin(radians);
const nx = (cos * (x - cx)) + (sin * (y - cy)) + cx;
const ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
return [nx, ny];
}
setDPI(canvas, 192);
class Star {
constructor() {
// Get a weighted random number, so that the majority of stars will form in the center of the orbit
const rands = [];
rands.push(Math.random() * (maxorbit / 2) + 1);
rands.push(Math.random() * (maxorbit / 2) + maxorbit);
this.orbital = (rands.reduce((p, c) => p + c, 0) / rands.length);
this.x = centerx; // All of these stars are at the center x position at all times
this.y = centery + this.orbital; // Set Y position starting at the center y + the position in the orbit
this.yOrigin = centery + this.orbital; // this is used to track the particles origin
this.speed = (Math.floor(Math.random() * 2.5) + 1.5) * Math.PI / 180; // The rate at which this star will orbit
this.rotation = 0; // current Rotation
this.startRotation = (Math.floor(Math.random() * 360) + 1) * Math.PI / 180; // Starting rotation
this.id = stars.length; // This will be used when expansion takes place
this.collapseBonus = this.orbital - (maxorbit * 0.7); // This "bonus" is used to randomly place some stars outside of the blackhole on hover
if (this.collapseBonus < 0) { // if the collapse "bonus" is negative
this.collapseBonus = 0; // set it to 0, this way no stars will go inside the blackhole
}
this.color = 'rgba(255,255,255,' + (1 - ((this.orbital) / 255)) + ')'; // Color the star white, but make it more transparent the further out it is generated
this.hoverPos = centery + (maxorbit / 2) + this.collapseBonus; // Where the star will go on hover of the blackhole
this.expansePos = centery + (this.id % 100) * -10 + (Math.floor(Math.random() * 20) + 1); // Where the star will go when expansion takes place
this.prevR = this.startRotation;
this.prevX = this.x;
this.prevY = this.y;
// Store original position for returning
this.originalY = this.yOrigin;
stars.push(this);
}
draw() {
if (!expanse && !returning) {
this.rotation = this.startRotation + (currentTime * this.speed);
if (!collapse) { // not hovered
if (this.y > this.yOrigin) {
this.y -= 2.5;
}
if (this.y < this.yOrigin - 4) {
this.y += (this.yOrigin - this.y) / 10;
}
} else { // on hover
this.trail = 1;
if (this.y > this.hoverPos) {
this.y -= (this.hoverPos - this.y) / -5;
}
if (this.y < this.hoverPos - 4) {
this.y += 2.5;
}
}
} else if (expanse && !returning) {
this.rotation = this.startRotation + (currentTime * (this.speed / 2));
if (this.y > this.expansePos) {
this.y -= Math.floor(this.expansePos - this.y) / -80; // Slower expansion for better visibility
}
} else if (returning) {
// Returning to original orbit slowly
this.rotation = this.startRotation + (currentTime * this.speed);
if (Math.abs(this.y - this.originalY) > 2) {
this.y += (this.originalY - this.y) / 50; // Much slower return
} else {
this.y = this.originalY;
this.yOrigin = this.originalY;
}
}
context.save();
context.fillStyle = this.color;
context.strokeStyle = this.color;
context.beginPath();
const oldPos = rotate(centerx, centery, this.prevX, this.prevY, -this.prevR);
context.moveTo(oldPos[0], oldPos[1]);
context.translate(centerx, centery);
context.rotate(this.rotation);
context.translate(-centerx, -centery);
context.lineTo(this.x, this.y);
context.stroke();
context.restore();
this.prevR = this.rotation;
this.prevX = this.x;
this.prevY = this.y;
}
}
// Event listeners
const centerHover = document.querySelector('.centerHover');
centerHover.addEventListener('click', function() {
collapse = false;
expanse = true;
returning = false;
this.classList.add('open');
// Start the return cycle after full expansion (20-30 seconds)
setTimeout(() => {
expanse = false;
returning = true;
// After particles return, reset to normal orbit
setTimeout(() => {
returning = false;
this.classList.remove('open');
}, 8000); // 8 seconds to return slowly
}, 25000); // 25 seconds of expansion experience
});
centerHover.addEventListener('mouseover', function() {
if (expanse === false) {
collapse = true;
}
});
centerHover.addEventListener('mouseout', function() {
if (expanse === false) {
collapse = false;
}
});
// Animation loop
function loop() {
const now = new Date().getTime();
currentTime = (now - startTime) / 50;
context.fillStyle = 'rgba(25,25,25,0.2)'; // somewhat clear the context, this way there will be trails behind the stars
context.fillRect(0, 0, cw, ch);
for (let i = 0; i < stars.length; i++) { // For each star
if (stars[i] !== undefined) {
stars[i].draw(); // Draw it
}
}
requestAnimationFrame(loop);
}
function init() {
context.fillStyle = 'rgba(25,25,25,1)'; // Initial clear of the canvas
context.fillRect(0, 0, cw, ch);
for (let i = 0; i < 2500; i++) { // create 2500 stars
new Star();
}
loop();
}
init();
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
blackhole('#blackhole');
});
|
'웹프로그램밍 자료실 > 기타 자료' 카테고리의 다른 글
| [해피CGI][cgimall] Electric Border (0) | 2025.12.22 |
|---|---|
| [해피CGI][cgimall] 로딩되기 전에 애니메이션 구현하기 Skeleton loading using only a few lines of CSS (0) | 2025.12.18 |
| [해피CGI][cgimall] 클릭하면 아이콘이 나오는 바 애니메이션 Chat Bar Interaction (0) | 2025.12.17 |
| [해피CGI][cgimall] 소음진동,대기질,온실가스 등 61종의 환경영향평가 데이터 제공 - OPEN API (0) | 2025.12.15 |
| [해피CGI][cgimall] designus 디자이너스 국내 ui/ux 벤치마킹 사이트 (0) | 2025.12.02 |
Comments

