All Prompts
All Prompts

animatedcanvastailwindvisualfull-screen
Animated Watercolor Botanical Background with Quote
Анимированный фон с ботаническими элементами в стиле акварели. Показывает цитату после отрисовки цветов. Реализован на Tailwind CSS и Canvas.
Prompt
<div class="relative w-full h-screen overflow-hidden select-none"
style="background-color: #faf8f3; font-family: 'Cormorant Garamond', serif;">
<script src="https://cdn.tailwindcss.com/3.4.17"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600&family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;1,400&display=swap');
.watercolor-canvas {
filter: url(#watercolor);
}
.paper-texture {
pointer-events: none;
mix-blend-mode: multiply;
opacity: 0.5;
}
.fade-in {
animation: fadeIn 3s forwards;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<!-- SVG Filters for Watercolor Effect -->
<svg class="absolute w-0 h-0">
<filter id="paper-noise">
<feTurbulence type="fractalNoise" baseFrequency="0.6" numOctaves="3" result="noise" />
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.4 0" />
</filter>
<filter id="watercolor">
<feTurbulence type="fractalNoise" baseFrequency="0.04" numOctaves="4" result="noise" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="14" />
<feGaussianBlur stdDeviation="0.6" />
</filter>
</svg>
<!-- Background Texture -->
<div class="paper-texture absolute inset-0 z-10">
<svg width="100%" height="100%">
<rect width="100%" height="100%" filter="url(#paper-noise)" />
</svg>
</div>
<div class="absolute inset-0 z-15 pointer-events-none"
style="background: radial-gradient(circle, transparent 60%, rgba(139, 119, 101, 0.15) 100%);"></div>
<!-- Canvas Layer -->
<canvas id="artCanvas" class="watercolor-canvas block absolute inset-0 z-0"></canvas>
<!-- UI Overlay -->
<div class="relative z-20 flex flex-col items-center pt-[8vh] text-[#2c3e50] pointer-events-none">
<h1 class="fade-in font-['Cinzel'] text-sm tracking-[0.4em] uppercase border-b border-black/20 pb-2 mb-4">Botanical
Study • Series IV</h1>
<div id="quoteText" class="fade-in opacity-0 italic text-2xl max-w-xl text-center leading-relaxed"
style="animation-delay: 2s;">
"The earth laughs in flowers."
<div class="text-sm not-italic tracking-wider opacity-60 mt-4">— Ralph Waldo Emerson</div>
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('artCanvas');
const ctx = canvas.getContext('2d');
let w, h, animationId;
let stems = [], flowers = [];
const colors = {
stems: [{h:85,s:35,l:45}, {h:120,s:25,l:38}, {h:65,s:40,l:55}],
petals: [{h:355,s:75,l:65}, {h:45,s:85,l:65}, {h:215,s:65,l:70}, {h:280,s:50,l:65}, {h:15,s:80,l:75}]
};
function resize() {
w = canvas.width = window.innerWidth;
h = canvas.height = window.innerHeight;
}
class Stem {
constructor(x) {
this.x = x;
this.y = h + 10;
this.segments = [];
this.len = h * (0.2 + Math.random() * 0.4);
this.curr = 0;
this.angle = -Math.PI/2 + (Math.random()-0.5)*0.3;
this.color = colors.stems[Math.floor(Math.random()*colors.stems.length)];
this.done = false;
this.flowerType = Math.floor(Math.random() * 4);
let cx = this.x, cy = this.y, ca = this.angle;
for(let i=0; i<this.len/5; i++) {
ca += (Math.random()-0.5)*0.1;
cx += Math.cos(ca)*5; cy += Math.sin(ca)*5;
this.segments.push({x:cx, y:cy, a:ca});
}
}
draw() {
if (this.curr < this.segments.length-1) {
const p = this.segments[Math.floor(this.curr)];
const n = this.segments[Math.floor(this.curr)+1];
ctx.beginPath();
ctx.moveTo(p.x, p.y); ctx.lineTo(n.x, n.y);
ctx.strokeStyle = `hsla(${this.color.h},${this.color.s}%,${this.color.l}%, 0.2)`;
ctx.lineWidth = 2 * (1 - this.curr/this.segments.length) + 1;
ctx.stroke();
this.curr += 1.5;
} else if (!this.done) {
this.done = true;
flowers.push(new Flower(this.segments[this.segments.length-1], this.flowerType));
}
}
}
class Flower {
constructor(pos, type) {
this.x = pos.x; this.y = pos.y; this.type = type;
this.age = 0; this.max = 80 + Math.random()*40;
this.clr = colors.petals[Math.floor(Math.random()*colors.petals.length)];
this.petals = Array.from({length: type === 0 ? 12 : 6}, (_, i) => ({
a: (i / (type === 0 ? 12 : 6)) * Math.PI * 2,
s: 0.8 + Math.random() * 0.5
}));
}
draw() {
if (this.age < this.max) {
this.age++;
const grow = Math.min(1, this.age/60);
this.petals.forEach(p => {
if (Math.random() > 0.5) return;
const r = (this.type === 0 ? 15 : 25) * grow;
const px = this.x + Math.cos(p.a) * r * 0.5;
const py = this.y + Math.sin(p.a) * r * 0.5;
ctx.beginPath();
ctx.ellipse(px, py, r * p.s, r * 0.6, p.a, 0, Math.PI*2);
ctx.fillStyle = `hsla(${this.clr.h},${this.clr.s}%,${this.clr.l}%, 0.04)`;
ctx.fill();
});
if (this.age > 30 && Math.random() > 0.8) {
ctx.beginPath();
ctx.arc(this.x, this.y, 3 * grow, 0, Math.PI*2);
ctx.fillStyle = `rgba(0,0,0,0.1)`; ctx.fill();
}
}
}
}
function init() {
resize();
stems = Array.from({length: Math.floor(w/30)}, () => new Stem(Math.random()*w));
flowers = [];
ctx.clearRect(0,0,w,h);
if (animationId) cancelAnimationFrame(animationId);
loop();
}
function loop() {
let active = false;
stems.forEach(s => { s.draw(); if(!s.done) active = true; });
flowers.forEach(f => { f.draw(); if(f.age < f.max) active = true; });
if (active) animationId = requestAnimationFrame(loop);
else document.getElementById('quoteText').classList.add('opacity-100');
}
window.addEventListener('resize', init);
init();
})();
</script>
</div>