일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 해피씨지아이
- happycgi
- 솔루션
- #업종별
- 웹솔루션
- #image
- #홈페이지제작
- #cgimall
- #CSS
- php
- #쇼핑몰
- #웹솔루션
- #jQuery
- 홈페이지제작
- #솔루션
- 게시판
- #뉴스
- CGIMALL
- CSS
- #이미지
- #홈페이지
- jquery
- #동영상
- 홈페이지
- javascript
- #해피CGI
- 해피CGI
- #happycgi
- 이미지
- 사이트제작
- Today
- Total
웹솔루션개발 25년 노하우! 해피CGI의 모든것
[해피CGI][cgimall] GenArt / Simplex noise 본문
JavaScript를 사용하여 캔버스에 노이즈 애니메이션을 추가하는 디자인 입니다.
노이즈를 추가하는 이미지가 있으면 사용하기 좋을거 같습니다.
자세한 내용은 데모를 참고해 주시기 바랍니다.
HTML
<script type="importmap">
{
"imports": {
"three/addons/": "https://unpkg.com/three@0.179.0/examples/jsm/"
}
}
</script>
<canvas id="cnv"/>
|
CSS
body{
overflow: hidden;
margin:0;
background-color: #ccc;
}
#cnv{
position: absolute;
top:50%;
left:50%;
transform: translate(-50%, -50%);
border: 3px solid #333;
border-radius: 150px;
background-color: #aab;
}
|
JS
import { Vector2 as vec2, MathUtils as mu, Clock } from "three";
import { SimplexNoise } from "three/addons/math/SimplexNoise.js";
console.clear();
// load fonts
await (async function () {
async function loadFont(fontface) {
await fontface.load();
document.fonts.add(fontface);
}
let fonts = [
new FontFace(
"MajorMonoDisplay",
"url(https://fonts.gstatic.com/s/majormonodisplay/v17/RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7DXeR.woff2) format('woff2')"
)
];
for (let font in fonts) {
await loadFont(fonts[font]);
}
})();
class ImgData{
constructor(grid, params){
let logoCnv = document.createElement("canvas");
logoCnv.width = grid.ctx.canvas.width;
logoCnv.height = grid.ctx.canvas.height;
//document.body.appendChild(logoCnv)
let logoCtx = logoCnv.getContext("2d");
let u = (val) => val * logoCnv.height * 0.01;
logoCtx.font = `${u(params.size)}px MajorMonoDisplay`;
logoCtx.fillStyle = params.color;
logoCtx.textAlign = "center";
logoCtx.textBaseline = "middle";
logoCtx.fillText(params.text, u(params.position.x), u(params.position.y));
this.logoData = logoCtx.getImageData(0, 0, logoCnv.width, logoCnv.height).data;
this.width = logoCnv.width;
this.height = logoCnv.height;
}
sampleData(u, v){
let x = Math.floor(u * this.width);
let y = Math.floor(v * this.height);
let dataIndex = (y * this.width + x) * 4;
return this.logoData.slice(dataIndex, dataIndex + 4);
}
fetchData(x, y){
let dataIndex = (y * this.width + x) * 4;
return this.logoData.slice(dataIndex, dataIndex + 4);
}
}
class Particle extends vec2 {
constructor(grid, x = 0, y = 0) {
super(x, y);
this.grid = grid;
this.speed = mu.randFloat(1, 1.1);
let step = 1 / grid.simplex.length;
let yIdx = Math.floor((this.y / this.grid.ctx.canvas.height) / step);
let xIdx = Math.floor((this.x / this.grid.ctx.canvas.width) / step);
this.noise = (/*xIdx +*/ yIdx) % this.grid.texture.length; //mu.randInt(0, this.grid.simplex.length - 1);
//console.log(step, this.noise);
this.color = 0;
this.opacity = 0;
this.thickness = 0;
this.initPos = new vec2(x, y);
}
draw() {
let ctx = this.grid.ctx;
let color = `hsla(${(this.color) * 360}, 0%, ${ 70 + 30 * this.color}%, ${this.opacity})`;
let tex = this.grid.texture[this.noise];
let fetchImgData = tex.fetchData(Math.floor(this.x), Math.floor(this.y));
color = (fetchImgData[3] == 0) ? color : `rgb(${fetchImgData[0]}, ${fetchImgData[1]}, ${fetchImgData[2]}, ${this.opacity})`;
ctx.fillStyle = color;
ctx.beginPath();
let x = this.x;
let y = this.y;
if (this.grid.rasterization == true){
let rst = this.grid.rasterizationStep;
//x = Math.floor(x / rst) * rst;
y = Math.floor(y / rst) * rst;
}
ctx.arc(x, y, this.grid.thickness + this.thickness, 0, Math.PI * 2);
ctx.fill();
}
}
class Grid {
constructor(
ctx,
params = {
spacing: 5, // how many pixels between points (grid) on x and y
quant: 0.001, // this.accumulate += quant
accumulateRatio: 0.4, // for this.accumulate
quantOpacity: 0.005, // increment of point's opacity per render
noiseAmount: 2,
coordRatio: 0.005, // the greater, the more density of noise
curliness: 1, // noise * Math.PI * curliness
rasterization: !!0, // if you want rasterization effect
rasterizationStep: 3,
thickness: 1, // point radius
anew: 1 // when this.accumulative is greater than `anew`'s value
}
) {
this.ctx = ctx;
this.hw = ctx.canvas.width * 0.5;
this.hh = ctx.canvas.height * 0.5;
this.spacing = params.spacing;
this.quant = params.quant;
this.accumulate = 0;
this.accumulateRatio = params.accumulateRatio;
this.quantOpacity = params.quantOpacity;
this.noiseAmount = params.noiseAmount;
this.coordRatio = params.coordRatio;
this.curliness = params.curliness;
this.rasterization = params.rasterization;
this.rasterizationStep = params.rasterizationStep;
this.thickness = params.thickness;
this.anew = params.anew;
this.particles = [];
this.texture = null;
this.simplex = null;
this.init();
}
init(){
this.texture = [
new ImgData(this, {text: "GEN", size: 45, position: new vec2(50, 30), color: "#e30"}),
new ImgData(this, {text: "ART", size: 45, position: new vec2(50, 70), color: "#000"})
];
this.setNoise();
for (let row = 0; row < Math.floor(cnv.height / this.spacing); row++) {
for (let col = 0; col < Math.floor(cnv.width / this.spacing); col++) {
let particle = new Particle(
this,
col * this.spacing,
row * this.spacing
);
particle.color = mu.clamp(particle.y / cnv.height, 0, 1) * 0.3 + 0.5;
this.particles.push(particle);
}
}
this.particles.reverse();
console.log(this.particles.length);
}
setNoise(){
this.simplex = Array.from({ length: this.noiseAmount }, (_, idx) => {
return new SimplexNoise();
});
}
update() {
this.accumulate += this.quant;
if (this.accumulate > this.anew){ // anew
this.accumulate = 0;
this.setNoise();
this.particles.forEach(p => {p.copy(p.initPos); p.opacity = 0;});
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
}
let hw = this.hw;
let hh = this.hh;
let cr = this.coordRatio;
this.particles.forEach((p) => {
let n = this.simplex[p.noise].noise3d(p.x * cr, p.y * cr, this.accumulate * p.speed * this.accumulateRatio);
//n = Math.sign(n) * (Math.abs(n)**0.5);
let a = n * this.curliness * Math.PI;
p.x += Math.cos(a);
p.y += Math.sin(a);
p.opacity += this.quantOpacity;
p.opacity = Math.min(p.opacity, 1);
//p.thickness = (n * 0.5 + 0.5) * 2;
// cycle the particle on a torus
if(p.x < 0) p.x = hw * 2;
if(p.x > hw * 2) p.x = 0;
if(p.y < 0) p.y = hh * 2;
if(p.y > hh * 2) p.y = 0;
////////////////////////////////
p.draw();
});
}
}
cnv.width = 800;
cnv.height = 800;
let ctx = cnv.getContext("2d");
let grid = new Grid(ctx);
(function draw() {
requestAnimationFrame(draw);
grid.update();
})();
|
'웹프로그램밍 자료실 > HTML 자료' 카테고리의 다른 글
[해피CGI][cgimall] 모바일에서 쓰기 좋은 애니메이션 메뉴 Mobile Menu Animation (0) | 2025.09.02 |
---|---|
[해피CGI][cgimall] 가격이 올라가는 카운터 애니메이션 (0) | 2025.08.27 |
[해피CGI][cgimall] Vue.js 반응형 셔플 이미지 갤러리 (0) | 2025.08.25 |
[해피CGI][cgimall] 가로 스크롤 갤러리- Horizontal Scrolling Gallery with ScrollTrigger (0) | 2025.08.22 |
[해피CGI][cgimall] Pure css popup box (0) | 2025.08.14 |
Comments