| 1 |
(() => {
|
| 2 |
: class="tok-val">cambia lo que quieras
|
| 3 |
class="tok-kw">const CONFIG = {
|
| 4 |
class="tok-prop">zanahoria: class="tok-val">"🥕",
|
| 5 |
class="tok-prop">conejo: class="tok-val">"🐇",
|
| 6 |
class="tok-prop">tamanoZanahoria: class="tok-val">18, |
| 7 |
class="tok-prop">tamanoConejo: class="tok-val">35, |
| 8 |
class="tok-prop">velocidad: class="tok-val">0.06, |
| 9 |
class="tok-prop">bocados: class="tok-val">5,
|
| 10 |
class="tok-prop">miraALaIzquierda: class="tok-val">true, |
| 11 |
class="tok-prop">segundosParaDormir: class="tok-val">5,
|
| 12 |
class="tok-prop">huellas: class="tok-val">"🐾",
|
| 13 |
class="tok-prop">corazones: class="tok-val">"🥕",
|
| 14 |
class="tok-prop">distanciaBoca: class="tok-val">24, |
| 15 |
|
| 16 |
class="tok-prop">inicial: class="tok-val">3, |
| 17 |
class="tok-prop">maximo: class="tok-val">10, |
| 18 |
class="tok-prop">porClic: class="tok-val">5,
|
| 19 |
class="tok-prop">maximo: class="tok-val">20,
|
| 20 |
};
|
| 21 |
|
| 22 |
|
| 23 |
class="tok-kw">if (matchMedia("(class="tok-prop">hover: class="tok-val">none)").matches) class="tok-kw">return;
|
| 24 |
class="tok-kw">if (matchMedia("(class="tok-prop">prefers-reduced-motion: class="tok-val">reduce)").matches) class="tok-kw">return;
|
| 25 |
|
| 26 |
|
| 27 |
class="tok-kw">const style = document.createElement("style");
|
| 28 |
style.textContent = `
|
| 29 |
*, *::before, *::after { class="tok-prop">cursor: class="tok-val">none !important; }
|
| 30 |
.cc-item { class="tok-prop">position: class="tok-val">fixed; class="tok-prop">top: class="tok-val">0; class="tok-prop">left: class="tok-val">0; class="tok-prop">pointer-events: class="tok-val">none; class="tok-prop">z-index: class="tok-val">99999; }
|
| 31 |
#cc-carrot { class="tok-prop">font-size: class="tok-val">${CONFIG.tamanoZanahoria}px; class="tok-prop">z-index: class="tok-val">100000; }
|
| 32 |
#cc-bunny { class="tok-prop">font-size: class="tok-val">${CONFIG.tamanoConejo}px; }
|
| 33 |
.cc-item span { class="tok-prop">display: class="tok-val">inline-block; }
|
| 34 |
.cc-fx { class="tok-prop">position: class="tok-val">fixed; class="tok-prop">pointer-events: class="tok-val">none; class="tok-prop">z-index: class="tok-val">99998; class="tok-prop">font-size: class="tok-val">18px; }
|
| 35 |
#cc-bunny.moving .hop { class="tok-prop">animation: class="tok-val">cc-hop .4s ease-in-out infinite; }
|
| 36 |
#cc-bunny.eating .hop { class="tok-prop">animation: class="tok-val">cc-munch .2s ease-in-out infinite; }
|
| 37 |
#cc-bunny.sleeping .hop { class="tok-prop">transform: class="tok-val">scale(1, .8) translateY(4px); class="tok-prop">filter: class="tok-val">grayscale(.5); }
|
| 38 |
#cc-bunny.jump .hop { class="tok-prop">animation: class="tok-val">cc-big .5s ease-out; }
|
| 39 |
|
| 40 |
#cc-counter { class="tok-prop">position: class="tok-val">fixed; class="tok-prop">top: class="tok-val">12px; class="tok-prop">right: class="tok-val">16px; class="tok-prop">font: class="tok-val">700 18px system-ui, sans-serif; class="tok-prop">z-index: class="tok-val">100001; class="tok-prop">pointer-events: class="tok-val">none;
|
| 41 |
class="tok-prop">color: class="tok-val">#fff; class="tok-prop">background: class="tok-val">rgba(0,0,0,.6); class="tok-prop">padding: class="tok-val">4px 12px; class="tok-prop">border-radius: class="tok-val">999px; }
|
| 42 |
#cc-carrot.empty { class="tok-prop">opacity: class="tok-val">0; }
|
| 43 |
/* más oscuro y apagadito */
|
| 44 |
#cc-bunny.hungry .hop { class="tok-prop">filter: class="tok-val">grayscale(1) brightness(.65); }
|
| 45 |
/* o más transparente
|
| 46 |
#cc-bunny.hungry .hop { class="tok-prop">opacity: class="tok-val">.55; } */
|
| 47 |
|
| 48 |
|
| 49 |
@keyframes cc-hop { 50% { class="tok-prop">transform: class="tok-val">translateY(-16px) rotate(-12deg); } }
|
| 50 |
@keyframes cc-munch { 50% { class="tok-prop">transform: class="tok-val">scale(1.15, .9); } }
|
| 51 |
@keyframes cc-big { 40% { class="tok-prop">transform: class="tok-val">translateY(-50px) rotate(-20deg); } }
|
| 52 |
@keyframes cc-fade { to { class="tok-prop">opacity: class="tok-val">0; } }
|
| 53 |
@keyframes cc-zzz { to { class="tok-prop">opacity: class="tok-val">0; class="tok-prop">transform: class="tok-val">translate(14px, -40px); } }
|
| 54 |
@keyframes cc-pop { to { class="tok-prop">opacity: class="tok-val">0; class="tok-prop">transform: class="tok-val">translate(class="tok-kw">var(--dx), class="tok-kw">var(--dy)); } }
|
| 55 |
|
| 56 |
`;
|
| 57 |
document.head.appendChild(style);
|
| 58 |
|
| 59 |
|
| 60 |
class="tok-kw">const carrot = document.createElement("div");
|
| 61 |
class="tok-kw">const bunny = document.createElement("div");
|
| 62 |
carrot.className = bunny.className = "cc-item";
|
| 63 |
carrot.id = "cc-carrot";
|
| 64 |
bunny.id = "cc-bunny";
|
| 65 |
|
| 66 |
carrot.innerHTML = `class="tok-tag"><spanclass="tok-attr">class="tok-tag">>${CONFIG.zanahoria}class="tok-tag"></spanclass="tok-attr">class="tok-tag">>`;
|
| 67 |
bunny.innerHTML = `class="tok-tag"><spanclass="tok-attr"> class="hop"class="tok-tag">>${CONFIG.conejo}class="tok-tag"></spanclass="tok-attr">class="tok-tag">>`;
|
| 68 |
document.body.append(carrot, bunny);
|
| 69 |
class="tok-kw">const piece = carrot.firstChild;
|
| 70 |
|
| 71 |
class="tok-kw">const counter = document.createElement("div");
|
| 72 |
counter.id = "cc-counter";
|
| 73 |
document.body.appendChild(counter);
|
| 74 |
|
| 75 |
class="tok-kw">let stock = CONFIG.inicial;
|
| 76 |
class="tok-kw">const updateCounter = () => {
|
| 77 |
counter.textContent = `${CONFIG.zanahoria} x${stock}`;
|
| 78 |
carrot.classList.toggle("empty", stock === 0);
|
| 79 |
bunny.classList.toggle("hungry", stock === 0);
|
| 80 |
};
|
| 81 |
updateCounter();
|
| 82 |
|
| 83 |
|
| 84 |
|
| 85 |
class="tok-kw">function fx(text, x, y, anim, extra = "") {
|
| 86 |
class="tok-kw">const el = document.createElement("div");
|
| 87 |
el.className = "cc-fx";
|
| 88 |
el.textContent = text;
|
| 89 |
el.style.cssText = `class="tok-prop">left:class="tok-val">${x}px; class="tok-prop">top:class="tok-val">${y}px; class="tok-prop">animation:class="tok-val">${anim}; ${extra}`;
|
| 90 |
el.addEventListener("animationend", () => el.remove());
|
| 91 |
document.body.appendChild(el);
|
| 92 |
}
|
| 93 |
|
| 94 |
class="tok-kw">let mx = innerWidth / 2, my = innerHeight / 2;
|
| 95 |
class="tok-kw">let bx = mx - 200, by = my;
|
| 96 |
class="tok-kw">let bites = 0, lastBite = 0, lastZ = 0, walked = 0;
|
| 97 |
class="tok-kw">let lastMove = performance.now();
|
| 98 |
|
| 99 |
class="tok-kw">const placeCarrot = () =>
|
| 100 |
(carrot.style.transform = `translate(${mx}px, ${my}px) translate(-50%, -50%)`);
|
| 101 |
placeCarrot();
|
| 102 |
|
| 103 |
class="tok-kw">function jump() {
|
| 104 |
bunny.classList.remove("jump");
|
| 105 |
void bunny.offsetWidth; |
| 106 |
bunny.classList.add("jump");
|
| 107 |
setTimeout(() => bunny.classList.remove("jump"), 500);
|
| 108 |
}
|
| 109 |
|
| 110 |
addEventListener("mousemove", (e) => {
|
| 111 |
mx = e.clientX; my = e.clientY;
|
| 112 |
lastMove = performance.now();
|
| 113 |
placeCarrot();
|
| 114 |
class="tok-kw">if (bunny.classList.contains("sleeping")) jump(); |
| 115 |
});
|
| 116 |
|
| 117 |
addEventListener("click", (e) => {
|
| 118 |
jump();
|
| 119 |
class="tok-kw">if (stock === 0) { bites = 0; piece.style.clipPath = "none"; }
|
| 120 |
stock = Math.min(stock + CONFIG.porClic, CONFIG.maximo);
|
| 121 |
updateCounter();
|
| 122 |
class="tok-kw">for (class="tok-kw">let i = 0; i < CONFIG.porClic; i++) {
|
| 123 |
class="tok-kw">const a = (Math.PI * 2 * i) / CONFIG.porClic;
|
| 124 |
fx(CONFIG.corazones, e.clientX, e.clientY, "cc-pop .8s ease-out forwards",
|
| 125 |
`class="tok-prop">--dx:class="tok-val">${Math.cos(a) * 50}px; class="tok-prop">--dy:class="tok-val">${Math.sin(a) * 50}px;`);
|
| 126 |
}
|
| 127 |
});
|
| 128 |
|
| 129 |
class="tok-kw">function loop(t) {
|
| 130 |
class="tok-kw">const dx = mx - bx, dy = my - by;
|
| 131 |
class="tok-kw">const dist = Math.hypot(dx, dy);
|
| 132 |
class="tok-kw">const near = dist < CONFIG.distanciaBoca + 15;
|
| 133 |
|
| 134 |
|
| 135 |
class="tok-kw">const d = dist || 1;
|
| 136 |
class="tok-kw">const tx = mx - (dx / d) * CONFIG.distanciaBoca;
|
| 137 |
class="tok-kw">const ty = my - (dy / d) * CONFIG.distanciaBoca;
|
| 138 |
|
| 139 |
class="tok-kw">const idle = t - lastMove > CONFIG.segundosParaDormir * 1000;
|
| 140 |
class="tok-kw">const sleeping = idle && near;
|
| 141 |
class="tok-kw">const eating = near && !sleeping && stock > 0 && bites < CONFIG.bocados;
|
| 142 |
|
| 143 |
bx += dx * CONFIG.velocidad;
|
| 144 |
by += dy * CONFIG.velocidad;
|
| 145 |
|
| 146 |
bunny.classList.toggle("sleeping", sleeping);
|
| 147 |
bunny.classList.toggle("eating", eating);
|
| 148 |
bunny.classList.toggle("moving", !near);
|
| 149 |
|
| 150 |
|
| 151 |
class="tok-kw">if (!near) {
|
| 152 |
walked += Math.hypot(dx, dy) * CONFIG.velocidad;
|
| 153 |
class="tok-kw">if (walked > 45) {
|
| 154 |
walked = 0;
|
| 155 |
fx(CONFIG.huellas, bx, by + 14, "cc-fade 1.5s forwards",
|
| 156 |
`class="tok-prop">transform: class="tok-val">translate(-50%, -50%) rotate(${Math.random() * 40 - 20}deg); class="tok-prop">filter: class="tok-val">grayscale(1);`);
|
| 157 |
}
|
| 158 |
}
|
| 159 |
|
| 160 |
|
| 161 |
class="tok-kw">if (sleeping && t - lastZ > 900) {
|
| 162 |
lastZ = t;
|
| 163 |
fx("z", bx + 10, by - 20, "cc-zzz 1.5s forwards");
|
| 164 |
}
|
| 165 |
|
| 166 |
|
| 167 |
class="tok-kw">if (eating && t - lastBite > 450) {
|
| 168 |
bites++;
|
| 169 |
lastBite = t;
|
| 170 |
piece.style.clipPath = `inset(0 0 0 ${(bites / CONFIG.bocados) * 100}%)`;
|
| 171 |
class="tok-kw">if (bites >= CONFIG.bocados) {
|
| 172 |
stock--;
|
| 173 |
updateCounter();
|
| 174 |
class="tok-kw">if (stock > 0) {
|
| 175 |
setTimeout(() => { bites = 0; piece.style.clipPath = "none"; }, 400);
|
| 176 |
}
|
| 177 |
}
|
| 178 |
}
|
| 179 |
|
| 180 |
class="tok-kw">if (stock === 0 && t - lastZ > 1200) {
|
| 181 |
lastZ = t;
|
| 182 |
fx("🥕?", bx + 10, by - 24, "cc-zzz 1.5s forwards");
|
| 183 |
}
|
| 184 |
|
| 185 |
class="tok-kw">const flip = (mx < bx) === CONFIG.miraALaIzquierda ? 1 : -1;
|
| 186 |
bunny.style.transform =
|
| 187 |
`translate(${bx}px, ${by}px) translate(-50%, -50%) scaleX(${flip})`;
|
| 188 |
requestAnimationFrame(loop);
|
| 189 |
}
|
| 190 |
requestAnimationFrame(loop);
|
| 191 |
})(); |