Тема для всякого-разного, вставляемого через html теги.
;;;;CSS / JS трюки :)
Сообщений 1 страница 8 из 8
Поделиться229.10.2025 20:20:57
Часы
[html]<div id="watch"><div class="frame-face"></div><ul class="minute-marks"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><ul class="digits"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul><div class="hours-hand"></div><div class="minutes-hand"></div><div class="seconds-hand"></div></div><style>body{font-size:62.5%;margin:1em;background:#232425}ul{list-style:none;margin:0;padding:0}#watch{font-size:1em;position:relative}#watch .frame-face{position:relative;width:30em;height:30em;margin:2em auto;border-radius:15em;background:-webkit-linear-gradient(top,#f9f9f9,#666);background:-moz-linear-gradient(top,#f9f9f9,#666);background:linear-gradient(to bottom,#f9f9f9,#666);box-shadow:rgba(0,0,0,.8) .5em .5em 4em}#watch .frame-face:before{content:'';width:29.4em;height:29.4em;border-radius:14.7em;position:absolute;top:.3em;left:.3em;background:-webkit-linear-gradient(135deg,rgba(246,248,249,0) 0%,rgba(229,235,238,1) 50%,rgba(205,212,217,1) 51%,rgba(245,247,249,0) 100%),-webkit-radial-gradient(center,ellipse cover,rgba(246,248,249,1) 0%,rgba(229,235,238,1) 65%,rgba(205,212,217,1) 66%,rgba(245,247,249,1) 100%);background:-moz-linear-gradient(135deg,rgba(246,248,249,0) 0%,rgba(229,235,238,1) 50%,rgba(205,212,217,1) 51%,rgba(245,247,249,0) 100%),-moz-radial-gradient(center,ellipse cover,rgba(246,248,249,1) 0%,rgba(229,235,238,1) 65%,rgba(205,212,217,1) 66%,rgba(245,247,249,1) 100%);background:linear-gradient(135deg,rgba(246,248,249,0) 0%,rgba(229,235,238,1) 50%,rgba(205,212,217,1) 51%,rgba(245,247,249,0) 100%),radial-gradient(ellipse at center,rgba(246,248,249,1) 0%,rgba(229,235,238,1) 65%,rgba(205,212,217,1) 66%,rgba(245,247,249,1) 100%)}#watch .frame-face:after{content:'';width:28em;height:28em;border-radius:14.2em;position:absolute;top:.9em;left:.9em;box-shadow:inset rgba(0,0,0,.2) .2em .2em 1em;border:.1em solid rgba(0,0,0,.2);background:-webkit-linear-gradient(top,#fff,#ccc);background:-moz-linear-gradient(top,#fff,#ccc);background:linear-gradient(to bottom,#fff,#ccc)}#watch .minute-marks li{display:block;width:.2em;height:.6em;background:#929394;position:absolute;top:50%;left:50%;margin:-.4em 0 0 -.1em}#watch .minute-marks li:first-child{transform:rotate(6deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(2){transform:rotate(12deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(3){transform:rotate(18deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(4){transform:rotate(24deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(5){transform:rotate(36deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(6){transform:rotate(42deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(7){transform:rotate(48deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(8){transform:rotate(54deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(9){transform:rotate(66deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(10){transform:rotate(72deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(11){transform:rotate(78deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(12){transform:rotate(84deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(13){transform:rotate(96deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(14){transform:rotate(102deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(15){transform:rotate(108deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(16){transform:rotate(114deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(17){transform:rotate(126deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(18){transform:rotate(132deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(19){transform:rotate(138deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(20){transform:rotate(144deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(21){transform:rotate(156deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(22){transform:rotate(162deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(23){transform:rotate(168deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(24){transform:rotate(174deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(25){transform:rotate(186deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(26){transform:rotate(192deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(27){transform:rotate(198deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(28){transform:rotate(204deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(29){transform:rotate(216deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(30){transform:rotate(222deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(31){transform:rotate(228deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(32){transform:rotate(234deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(33){transform:rotate(246deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(34){transform:rotate(252deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(35){transform:rotate(258deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(36){transform:rotate(264deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(37){transform:rotate(276deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(38){transform:rotate(282deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(39){transform:rotate(288deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(40){transform:rotate(294deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(41){transform:rotate(306deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(42){transform:rotate(312deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(43){transform:rotate(318deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(44){transform:rotate(324deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(45){transform:rotate(336deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(46){transform:rotate(342deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(47){transform:rotate(348deg) translateY(-12.7em)}#watch .minute-marks li:nth-child(48){transform:rotate(354deg) translateY(-12.7em)}#watch .digits{width:30em;height:30em;border-radius:15em;position:absolute;top:0;left:50%;margin-left:-15em}#watch .digits li{font-size:1.6em;display:block;width:1.6em;height:1.6em;position:absolute;top:50%;left:50%;line-height:1.6em;text-align:center;margin:-.8em 0 0 -.8em;font-weight:bold}#watch .digits li:nth-child(1){transform:translate(3.9em,-6.9em)}#watch .digits li:nth-child(2){transform:translate(6.9em,-4em)}#watch .digits li:nth-child(3){transform:translate(8em,0)}#watch .digits li:nth-child(4){transform:translate(6.8em,4em)}#watch .digits li:nth-child(5){transform:translate(3.9em,6.9em)}#watch .digits li:nth-child(6){transform:translate(0,8em)}#watch .digits li:nth-child(7){transform:translate(-3.9em,6.9em)}#watch .digits li:nth-child(8){transform:translate(-6.8em,4em)}#watch .digits li:nth-child(9){transform:translate(-8em,0)}#watch .digits li:nth-child(10){transform:translate(-6.9em,-4em)}#watch .digits li:nth-child(11){transform:translate(-3.9em,-6.9em)}#watch .digits li:nth-child(12){transform:translate(0,-8em)}#watch .digits:before{content:'';width:1.6em;height:1.6em;border-radius:.8em;position:absolute;top:50%;left:50%;margin:-.8em 0 0 -.8em;background:#121314}#watch .digits:after{content:'';width:4em;height:4em;border-radius:2.2em;position:absolute;top:50%;left:50%;margin:-2.1em 0 0 -2.1em;border:.1em solid #c6c6c6;background:-webkit-radial-gradient(center,ellipse cover,rgba(200,200,200,0),rgba(190,190,190,1) 90%,rgba(130,130,130,1) 100%);background:-moz-radial-gradient(center,ellipse cover,rgba(200,200,200,0),rgba(190,190,190,1) 90%,rgba(130,130,130,1) 100%);background:radial-gradient(ellipse at center,rgba(200,200,200,0),rgba(190,190,190,1) 90%,rgba(130,130,130,1) 100%)}#watch .hours-hand{width:.8em;height:7em;border-radius:0 0 .9em .9em;background:#232425;position:absolute;bottom:50%;left:50%;margin:0 0 -.8em -.4em;box-shadow:#232425 0 0 2px;transform-origin:.4em 6.2em}#watch .hours-hand:before{content:'';background:inherit;width:1.8em;height:.8em;border-radius:0 0 .8em .8em;box-shadow:#232425 0 0 1px;position:absolute;top:-.7em;left:-.5em}#watch .hours-hand:after{content:'';width:0;height:0;border:.9em solid #232425;border-width:0 .9em 2.4em .9em;border-left-color:transparent;border-right-color:transparent;position:absolute;top:-3.1em;left:-.5em}#watch .minutes-hand{width:.8em;height:12.5em;border-radius:.5em;background:#343536;position:absolute;bottom:50%;left:50%;margin:0 0 -1.5em -.4em;box-shadow:#343536 0 0 2px;transform-origin:.4em 11em}#watch .seconds-hand{width:.2em;height:14em;border-radius:.1em .1em 0 0/10em 10em 0 0;background:#c00;position:absolute;bottom:50%;left:50%;margin:0 0 -2em -.1em;box-shadow:rgba(0,0,0,.8) 0 0 .2em;transform-origin:.1em 12em}#watch .seconds-hand:after{content:'';width:1.4em;height:1.4em;border-radius:.7em;background:inherit;position:absolute;left:-.65em;bottom:1.35em}#watch .seconds-hand:before{content:'';width:.8em;height:3em;border-radius:.2em .2em .4em .4em/.2em .2em 2em 2em;box-shadow:rgba(0,0,0,.8) 0 0 .2em;background:inherit;position:absolute;left:-.35em;bottom:-3em}</style><script>function updateWatch(){const now=new Date(),hours=now.getHours()%12,minutes=now.getMinutes(),seconds=now.getSeconds(),hoursRotation=(hours+minutes/60)*30,minutesRotation=(minutes+seconds/60)*6,secondsRotation=seconds*6;document.querySelector('.hours-hand').style.transform=`rotate(${hoursRotation}deg)`,document.querySelector('.minutes-hand').style.transform=`rotate(${minutesRotation}deg)`,document.querySelector('.seconds-hand').style.transform=`rotate(${secondsRotation}deg)`}setInterval(updateWatch,1000),updateWatch();</script>[/html]
Поделиться329.10.2025 21:30:54
Тема для всякого-разного, вставляемого через html теги.
темка хорошА, я тоже люблю всякие "оживляшки", иногда практикуюсь на девочке, которая вынуждена сопровождать мою работу на стороне клиента
Так сказать, back-end.
И чтоб ей скучно не было, я запускал разные приколы... твой паучок в углу - как раз из этой серии. Иногда просто дразнил ее, находящуюся на работе в вечной спешке (она вообще-то логист, а тут еще и сайт ей навалили) - делал админку в стиле пляжного отдыха под пальмами (SVG, CSS-анимация, немного JS). А уж часики... или разноцветные птицы (SVG mask), летающие через экран... короче, не думаю, что она отдыхала, заходя в админку. Но от работы отвлекалась точно 
Сейчас настали черные дни в работе моего заказчика. И буквально 6 часов назад я загрузил механизм, позволяющий убить стартовую страницу сайта. На которой цены. И они это сделали - теперь там нет цен, звоните и узнавайте, все ручками. Такая вот извращенная конкуренция нынче - в эпоху стоящего насмерть экспорта, параллельного импорта и теневого флота (могу пояснить детали, ибо это - не фигура речи). Ну и отсутствия экономической активности... как сказал мне сегодня их директор в телефонной беседе "никто не покупает, нет производства и крупных строек, все только продовольствие покупают... я тоже".
И на этом фоне... я перестал думать о веселье на back-end'e. Ну а на фронт-энде (твои часики) - почему бы нет?
- Подпись автора
"Я не собираюсь расшифровывать, шиза это или такое чувство юмора. По умолчанию - шиза."(С)
Поделиться429.10.2025 21:44:00
upd: девизом в моей админке (первой строчкой с хорошим шрифтом и стилем) служит строка из Библии (сам-то я неверующий, если помнишь):
"и ищущий находит, и стучащемуся отворят" (Лк 11:10)
Что означает - не бойтесь спрашивать, узнавать новое, пробовать применить... В моем понимании, ессно.
- Подпись автора
"Я не собираюсь расшифровывать, шиза это или такое чувство юмора. По умолчанию - шиза."(С)
Поделиться529.10.2025 22:08:22
Туман на фотографии, который убирается:
⊹ курсором 🐭мыши на 💻компе
👇🏻пальцем на 📲сенсорных устройствах
Это всего лишь один класс CSS (filter). Кстати, меня всегда забавляла реализация псевдокласса :hover на touchscreen. Она всегда с чем-нить конфликтует. Так что, не советую ее использовать.
Отредактировано Severin (29.10.2025 22:20:25)
- Подпись автора
"Я не собираюсь расшифровывать, шиза это или такое чувство юмора. По умолчанию - шиза."(С)
Поделиться629.10.2025 23:03:59
Туман на фотографии, который убирается:
⊹ курсором 🐭мыши на 💻компе
👇🏻пальцем на 📲сенсорных устройствах
[html]<div class="fog-container"><canvas id="fog"></canvas><canvas id="fog-bg"></canvas><canvas id="brush"></canvas><svg id="brush-cursor" xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><circle cx="24" cy="24" r="20" fill="none" stroke="white" stroke-width="2" stroke-opacity="0.7"/><circle cx="24" cy="24" r="12" fill="white" fill-opacity="0.15"/></svg></div><button id="reset-fog" type="button">Вернуть туман</button><style>
.fog-container{aspect-ratio:16/9;position:relative;overflow:hidden;margin:20px 0;border-radius:6px;touch-action:none;width:100%;cursor:none}
@media (min-width:768px){.fog-container{max-width:60rem;margin-left:auto;margin-right:auto}}
.fog-container canvas{display:block;width:100%;height:100%;position:absolute;left:50%;top:0;transform:translateX(-50%)}
#brush{opacity:0}
#brush-cursor{position:fixed;pointer-events:none;width:40px;height:40px;margin:0;top:0;left:0;z-index:1000;opacity:0;transition:opacity 0.15s ease;transform:translate(-50%,-50%);filter:drop-shadow(0 0 3px rgba(0,0,0,0.4))}
#reset-fog{display:block;margin:20px auto;padding:12px 24px;background:rgba(0,0,0,0.3);color:white;border:1px solid rgba(255,255,255,0.7);border-radius:6px;cursor:pointer;font-size:16px;font-weight:500;backdrop-filter:blur(4px);outline:none}
@media (hover:hover) and (pointer:fine){
#reset-fog::before{content:'';position:absolute;bottom:0;left:0;width:100%;height:100%;background:rgba(255,255,255,0.15);backdrop-filter:blur(4px);transform:translateY(100%);transition:transform 0.4s cubic-bezier(0.22,0.61,0.36,1);z-index:0}
#reset-fog span{position:relative;z-index:1}
#reset-fog:hover{border-color:rgba(255,255,255,0.8)}
#reset-fog:hover::before{transform:translateY(0)}
}
#reset-fog:active{border-color:rgba(255,255,255,0.9);background:rgba(0,0,0,0.4);transform:scale(0.98);transition:transform 0.1s ease}
#reset-fog:focus-visible{outline:2px solid #fff;outline-offset:2px}
@media (max-width:480px){#reset-fog{padding:9px 20px;font-size:15px}}
@media (pointer:coarse){.fog-container{cursor:auto!important}#brush-cursor{display:none!important}}
</style><script>
class FogParticle {
constructor(ctx, canvasWidth, canvasHeight) {
this.ctx = ctx;
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
this.x = 0;
this.y = 0;
}
setPosition(x, y) { this.x = x; this.y = y; }
setVelocity(x, y) { this.xVelocity = x; this.yVelocity = y; }
setImage(image) { this.image = image; }
render() {
if (!this.image) return;
this.ctx.drawImage(this.image, this.x - this.image.width / 2, this.y - this.image.height / 2, 400, 400);
this.x += this.xVelocity; this.y += this.yVelocity;
if (this.x >= this.canvasWidth) { this.xVelocity = -this.xVelocity; this.x = this.canvasWidth; }
else if (this.x <= 0) { this.xVelocity = -this.xVelocity; this.x = 0; }
if (this.y >= this.canvasHeight) { this.yVelocity = -this.yVelocity; this.y = this.canvasHeight; }
else if (this.y <= 0) { this.yVelocity = -this.yVelocity; this.y = 0; }
}
}
class Fog {
constructor({ selector, density = 50, velocity = 2, particle, bgi } = {}) {
const canvas = document.querySelector(selector);
const bcr = canvas.parentElement.getBoundingClientRect();
this.ctx = canvas.getContext('2d');
this.canvasWidth = canvas.width = bcr.width;
this.canvasHeight = canvas.height = bcr.height;
this.particleCount = density;
this.maxVelocity = velocity;
this.particle = particle;
this.bgi = bgi;
this._createParticles();
this._setImage();
if (!this.bgi) return;
const img = new Image();
img.onload = () => {
const size = coverImg(img, this.canvasWidth, this.canvasHeight);
this.bgi = { img, w: size.w, h: size.h };
this._render();
};
img.src = this.bgi;
}
_createParticles() {
this.particles = [];
const random = (min, max) => Math.random() * (max - min) + min;
for (let i = 0; i < this.particleCount; i++) {
const particle = new FogParticle(this.ctx, this.canvasWidth, this.canvasHeight);
particle.setPosition(random(0, this.canvasWidth), random(0, this.canvasHeight));
particle.setVelocity(random(-this.maxVelocity, this.maxVelocity), random(-this.maxVelocity, this.maxVelocity));
this.particles.push(particle);
}
}
_setImage() {
if (!this.particle) return;
const img = new Image();
img.onload = () => this.particles.forEach(p => p.setImage(img));
img.src = this.particle;
}
_render() {
if (this.bgi) {
this.ctx.drawImage(this.bgi.img, 0, 0, this.bgi.w, this.bgi.h);
} else {
this.ctx.fillStyle = "rgba(0, 0, 0, 1)";
this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
}
this.particles.forEach(p => p.render());
requestAnimationFrame(this._render.bind(this));
}
}
class Eraser {
constructor({ bgCanvas, brushCanvas, bgi, radius = 120 } = {}) {
this.bgCanvas = document.querySelector(bgCanvas);
this.brushCanvas = document.querySelector(brushCanvas);
this.bgCtx = this.bgCanvas.getContext('2d');
this.brushCtx = this.brushCanvas.getContext('2d');
this.parentElement = this.bgCanvas.parentElement;
const bcr = this.parentElement.getBoundingClientRect();
this.canvasWidth = this.bgCanvas.width = this.brushCanvas.width = bcr.width;
this.canvasHeight = this.bgCanvas.height = this.brushCanvas.height = bcr.height;
this.brushRadius = radius;
this.bgi = new Image();
this.bgi.src = bgi;
this.bgi.onload = this._attachEvents.bind(this);
const bgCanvasEl = this.bgCanvas;
this.utils = {
distanceBetween(point1, point2) {
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
},
angleBetween(point1, point2) {
return Math.atan2(point2.x - point1.x, point2.y - point1.y);
},
getMousePos(e) {
const bcr = bgCanvasEl.getBoundingClientRect();
return { x: e.clientX - bcr.left, y: e.clientY - bcr.top };
},
getTouchPos(touch) {
const bcr = bgCanvasEl.getBoundingClientRect();
return { x: touch.clientX - bcr.left, y: touch.clientY - bcr.top };
}
};
}
_attachEvents() {
const parent = this.parentElement;
parent.addEventListener('mousemove', this._onMouseMove.bind(this));
parent.addEventListener('mouseleave', this._onMouseLeave.bind(this));
parent.addEventListener('touchstart', this._onTouchStart.bind(this), { passive: false });
parent.addEventListener('touchmove', this._onTouchMove.bind(this), { passive: false });
parent.addEventListener('touchend', this._onTouchEnd.bind(this), { passive: false });
}
_onMouseMove(e) {
const currentPoint = this.utils.getMousePos(e);
this._drawStroke(currentPoint);
}
_onMouseLeave() { this.lastPoint = null; }
_onTouchStart(e) {
e.preventDefault();
const touch = e.touches[0];
const pos = this.utils.getTouchPos(touch);
this.lastPoint = pos;
}
_onTouchMove(e) {
e.preventDefault();
const touch = e.touches[0];
const currentPoint = this.utils.getTouchPos(touch);
this._drawStroke(currentPoint);
}
_onTouchEnd(e) {
e.preventDefault();
this.lastPoint = null;
}
_drawStroke(currentPoint) {
this.lastPoint = this.lastPoint || currentPoint;
const dist = this.utils.distanceBetween(this.lastPoint, currentPoint);
const angle = this.utils.angleBetween(this.lastPoint, currentPoint);
for (let ii = 0; ii < dist; ii += 5) {
const x = this.lastPoint.x + (Math.sin(angle) * ii);
const y = this.lastPoint.y + (Math.cos(angle) * ii);
const brush = this.brushCtx.createRadialGradient(x, y, 0, x, y, this.brushRadius);
brush.addColorStop(0, 'rgba(0, 0, 0, 1)');
brush.addColorStop(0.3, 'rgba(0, 0, 0, 0.1)');
brush.addColorStop(1, 'rgba(0, 0, 0, 0)');
this.brushCtx.fillStyle = brush;
this.brushCtx.fillRect(x - this.brushRadius, y - this.brushRadius, this.brushRadius * 2, this.brushRadius * 2);
}
this.lastPoint = currentPoint;
this.bgCtx.globalCompositeOperation = 'source-over';
const size = coverImg(this.bgi, this.canvasWidth, this.canvasHeight);
this.bgCtx.drawImage(this.bgi, 0, 0, size.w, size.h);
this.bgCtx.globalCompositeOperation = 'destination-in';
this.bgCtx.drawImage(this.brushCanvas, 0, 0);
}
}
const coverImg = (img, width, height) => {
const ratio = img.width / img.height;
let w = width;
let h = w / ratio;
if (h < height) { h = height; w = h * ratio; }
return { w, h };
};
const bgi = 'https://upforme.ru/uploads/001a/f0/7d/2/35680.webp';
function getFogDensity() {
if (window.innerWidth < 480) return 25;
if (window.innerWidth < 768) return 35;
return 80;
}
function getEraserRadius() {
if (window.innerWidth < 480) return 40;
if (window.innerWidth < 768) return 55;
return 60;
}
function resize() {
new Fog({ selector: '#fog', particle: 'https://upforme.ru/uploads/001a/f0/7d/2/814636.png', density: getFogDensity(), bgi });
new Eraser({ bgCanvas: '#fog-bg', brushCanvas: '#brush', radius: getEraserRadius(), bgi });
}
resize();
window.addEventListener("resize", resize);
const customCursor = document.getElementById('brush-cursor');
const fogContainer = document.querySelector('.fog-container');
if (customCursor && fogContainer && window.matchMedia('(pointer: fine)').matches) {
const updateCursor = (e) => {
customCursor.style.left = e.clientX + 'px';
customCursor.style.top = e.clientY + 'px';
customCursor.style.opacity = '1';
};
fogContainer.addEventListener('mousemove', updateCursor);
fogContainer.addEventListener('mouseenter', () => { customCursor.style.opacity = '1'; });
fogContainer.addEventListener('mouseleave', () => { customCursor.style.opacity = '0'; });
} else if (customCursor) {
customCursor.style.display = 'none';
}
document.getElementById('reset-fog')?.addEventListener('click', () => {
['fog', 'fog-bg', 'brush'].forEach(id => {
const canvas = document.getElementById(id);
if (canvas) {
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
resize();
});
</script>[/html]
Поделиться729.10.2025 23:15:36
Не, это веселее, конечно же.
- Подпись автора
"Я не собираюсь расшифровывать, шиза это или такое чувство юмора. По умолчанию - шиза."(С)
Поделиться830.10.2025 02:27:52
Галерея фотографий в виде колоды карт
[html]
<div class="cart_container">
<div class="cart">
<div class="img-wrap"><img src="https://upforme.ru/uploads/001a/f0/7d/57/591695.jpg" alt="" /></div>
</div>
<div class="cart">
<div class="img-wrap"><img src="https://upforme.ru/uploads/001a/f0/7d/57/865869.jpg" alt="" /></div>
</div>
<div class="cart">
<div class="img-wrap"><img src="https://upforme.ru/uploads/001a/f0/7d/57/492941.jpg" alt="" /></div>
</div>
<div class="cart">
<div class="img-wrap"><img src="https://upforme.ru/uploads/001a/f0/7d/57/119023.jpg" alt="" /></div>
</div>
<div class="cart">
<div class="img-wrap"><img src="https://upforme.ru/uploads/001a/f0/7d/57/961457.jpg" alt="" /></div>
</div>
<div class="cart">
<div class="img-wrap"><img src="https://upforme.ru/uploads/001a/f0/7d/57/502938.jpg" alt="" /></div>
</div>
</div>
<style>
/*
📦 Контейнер всей колоды. Позиционируется относительно, чтобы .cart с position: absolute
были привязаны именно к нему, а не к body.
*/
.cart_container {
margin: 20px 0;
position: relative;
}
/*
🃏 Стиль одной "карты". Все карты наложены друг на друга в центре.
Высота и ширина фиксированы: 400×300px — это размер "рамки" карты.
*/
.cart {
height: 400px;
width: 300px;
position: absolute;
top: 0;
/* Центрирование по горизонтали: 50% экрана минус половина ширины карты (150px) */
left: calc(50% - 150px);
/*
⚠️ ВАЖНО: overflow: visible — чтобы повёрнутые уголки не обрезались!
Обрезка изображения происходит внутри .img-wrap, а не здесь.
*/
overflow: visible;
/* Тень и рамка для объёма */
box-shadow: 0 4px 12px rgba(0,0,0,0.1), 0 10px 20px rgba(0,0,0,0.1);
cursor: pointer;
border-radius: 10px; /* Скруглённые углы */
border: 1px solid #337AB7; /* Синяя рамка */
/*
🖼️ padding создаёт "паспарту" — фоновую полоску (#BFE2FF) вокруг изображения.
Без него изображение вплотную прилегает к краю, и скруглённые углы становятся невидимы.
*/
padding: 10px;
z-index: 100;
background-color: #BFE2FF; /* Цвет паспарту */
}
/*
🌀 Каждой карте задаётся индивидуальный поворот для эффекта "разбросанной колоды".
У 3-й карты самый сильный поворот (8.5°), поэтому именно у неё чаще всего обрезаются уголки.
*/
.cart:nth-child(1) {
transform: rotate(-3deg);
position: relative; /* Только первая карта — relative, чтобы не мешать stacking context */
}
.cart:nth-child(2) { transform: rotate(4deg); }
.cart:nth-child(3) { transform: rotate(8.5deg); } /* ← максимальный поворот */
.cart:nth-child(4) { transform: rotate(-6deg); }
.cart:nth-child(5) { transform: rotate(-2deg); }
.cart:nth-child(6) { transform: rotate(7deg); }
/*
🖼️ Обёртка для изображения.
Здесь происходит обрезка изображения, чтобы оно не вылезало за скруглённые углы.
border-radius чуть меньше (6px), чтобы не касаться синей рамки карты.
*/
.img-wrap {
width: 100%;
height: 100%;
overflow: hidden;
border-radius: 6px;
}
/*
📸 Изображение внутри карты.
object-fit: cover — масштабирует фото с сохранением пропорций так, чтобы полностью
заполнить область 300×400px. При этом часть изображения может обрезаться по краям,
если его пропорции не совпадают с 3:4.
*/
.cart img {
width: 100%;
height: 100%;
/*
* Режим масштабирования изображения:
* • cover — заполняет всю область (может обрезать края, но без пустот) ← используется сейчас
* • contain — показывает всё изображение (без обрезки, но могут быть пустые полосы)
* • fill — растягивает без пустот и обрезки, но искажает пропорции (не рекомендуется)
*/
object-fit: cover;
display: block;
}
/*
🎭 Классы анимации: добавляются при клике.
bottom — для всех карт, кроме последней в цикле.
bottom_last — для последней карты (чтобы после неё сбросить порядок).
*/
.bottom {
z-index: 50;
animation: move ease-in-out 1s forwards;
}
.bottom_last {
z-index: 30;
animation: move_last ease-in-out 1s forwards;
}
/*
🔄 Анимация "ухода вправо и возврата на место".
В середине анимации карта сдвигается вправо на 220px (всё ещё внутри контейнера),
а z-index меняется, чтобы визуально карта уходила "вниз колоды".
*/
@keyframes move {
0% {
left: calc(50% - 150px);
z-index: 150; /* временно поднимаем наверх для плавного старта */
}
50% {
left: calc(50% + 220px); /* улетает вправо */
}
100% {
left: calc(50% - 150px); /* возвращается на место */
z-index: 50; /* остаётся внизу колоды */
}
}
@keyframes move_last {
0% {
left: calc(50% - 150px);
z-index: 150;
}
50% {
left: calc(50% + 220px);
z-index: 50;
}
100% {
left: calc(50% - 150px);
z-index: 30; /* самая нижняя позиция */
}
}
/*
📱 Мобильная адаптация: карта становится уже (260px), чтобы повёрнутые края
не вылезали за границы узкого экрана. Центрирование сохраняется.
*/
@media (max-width: 768px) {
.cart {
width: 260px;
height: calc(260px * 4 / 3);
left: calc(50% - 130px); /* 260 / 2 = 130 */
}
/* Анимации адаптируются под новую ширину */
@keyframes move {
0% { left: calc(50% - 130px); z-index: 150; }
50% { left: calc(50% + 180px); }
100% { left: calc(50% - 130px); z-index: 50; }
}
@keyframes move_last {
0% { left: calc(50% - 130px); z-index: 150; }
50% { left: calc(50% + 180px); z-index: 50; }
100% { left: calc(50% - 130px); z-index: 30; }
}
}
</style>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function () {
let count = 1; // 🔢 Счётчик кликов (начинается с 1)
let click = true; // 🛑 Флаг блокировки повторных кликов во время анимации
let num = $(".cart").length; // 🃏 Общее количество карт
$(".cart").click(function() {
if (!click) return; // Если анимация идёт — игнорируем клик
click = false;
// Добавляем нужный класс анимации
if (count < num) {
$(this).addClass("bottom");
count++;
} else {
$(this).addClass("bottom_last");
count++;
}
// После полного цикла (6 кликов) — сбрасываем все классы и счётчик
if (count === num + 1) {
setTimeout(function () {
$(".cart").removeClass("bottom bottom_last");
count = 1;
}, 1000);
}
// Разблокируем клики через 1 секунду (длительность анимации)
setTimeout(function () {
click = true;
}, 1000);
});
});
</script>
[/html]


