Kış sezonu, yılbaşı kampanyaları ve sezonluk indirimler için sitenize küçük ama etkili bir dokunuş yapmak istiyorsanız, kar yağma efekti harika bir tercih. Bu yazıda, İkas altyapılı e-ticaret sitenize ekstra uygulama kullanmadan, yalnızca JavaScript ile tamamen özelleştirilebilir ve performanslı bir kar efekti eklemeyi adım adım anlatıyorum.
Hazırladığımız çözüm;
- Sadece kar tanesi değil, isteğe bağlı yıldız, hediye ve ışıltı parçacıkları destekler
- Mobilde otomatik optimize olur
- Kullanıcının “hareket azalt” tercihini dikkate alır (erişilebilirlik)
- Tek satırla aç/kapa / ayar değiştirme imkânı verir
1. Adım: İkas Script Alanına Ulaşın
Önce kodu ekleyeceğimiz alanı açıyoruz:
- İkas panelinize giriş yapın.
- Sol menüden sırasıyla:
Satış Kanalları → Eklentiler → Scriptler menüsüne gidin. - Sağ üstteki “Script Ekle” butonuna tıklayın.
- Script İsmi alanına dilediğiniz bir başlık yazabilirsiniz (örneğin:
Yılbaşı Kar Efekti). - Script İçeriği alanına birazdan paylaşacağım kodu (veya indirdiğiniz dosyadaki metni) eksiksiz şekilde yapıştırın.
Kaydettiğinizde script, ilgili satış kanalında otomatik olarak devreye girecektir.
2. Adım: Kar Efekti Kodunu Script İçeriği Alanına Ekleyin
Aşağıdaki kodu, İkas Scriptler > Script İçeriği alanına tek parça olarak yapıştırın:
<script>
/**
* İkas Yılbaşı & Kar Yağışı Efekti
* - Canvas tabanlı, performanslı
* - Erişilebilirlik ve mobil optimizasyonlu
*/
function createChristmasSnowEffect(options = {}) {
// Mobil cihaz kontrolü
const isMobile =
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
) || window.innerWidth < 768;
// Hareket hassasiyeti (erişilebilirlik)
const prefersReducedMotion = window
.matchMedia("(prefers-reduced-motion: reduce)")
.matches;
// Kullanıcı hareket azaltma tercih ediyorsa, efekti çalıştırma
if (prefersReducedMotion && options.respectMotionPreference !== false) {
return {
stop: () => {},
updateSettings: () => {},
pause: () => {},
resume: () => {},
};
}
const settings = {
particleCount:
options.partikülSayısı ||
options.particleCount ||
(isMobile ? 30 : 60),
enableStars:
options.yıldızlarıAktifEt !== false && options.enableStars !== false,
enableGifts:
options.hediyeleriAktifEt !== false && options.enableGifts !== false,
enableSparkles:
options.ışıltılarıAktifEt !== false && options.enableSparkles !== false,
enablePulse:
options.parlamaEfekti !== false && options.enablePulse !== false,
sizeMultiplier: options.partikülBoyutu || options.sizeMultiplier || 1,
zIndex: options.zIndex || 9999,
respectMotionPreference:
options.hareketAzaltmaTercihi !== false &&
options.respectMotionPreference !== false,
};
const canvas = document.createElement("canvas");
canvas.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: ${settings.zIndex};
background: none;
opacity: 1;
`;
canvas.setAttribute("aria-hidden", "true");
canvas.setAttribute("role", "presentation");
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d", { alpha: true });
let particles = [];
let animationId;
let isPageVisible = true;
let isPaused = false;
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
// Sekme görünürlüğü değişince animasyonu durdur / devam ettir
const handleVisibilityChange = () => {
isPageVisible = !document.hidden;
if (!isPageVisible) {
cancelAnimationFrame(animationId);
} else if (!isPaused) {
animate();
}
};
document.addEventListener("visibilitychange", handleVisibilityChange);
// Parçacık sınıfı
class Particle {
constructor() {
this.types = ["snowflake", "star", "gift", "sparkle"];
this.weights = [0.6, 0.15, 0.1, 0.15]; // tip olasılıkları
this.reset();
this.y = Math.random() * canvas.height;
this.rotation = Math.random() * Math.PI * 2;
}
selectType() {
const rand = Math.random();
let cumulative = 0;
if (!settings.enableStars) this.weights[1] = 0;
if (!settings.enableGifts) this.weights[2] = 0;
if (!settings.enableSparkles) this.weights[3] = 0;
const total = this.weights.reduce((a, b) => a + b, 0);
for (let i = 0; i < this.types.length; i++) {
cumulative += this.weights[i] / total;
if (rand < cumulative) {
return this.types[i];
}
}
return "snowflake";
}
reset() {
this.type = this.selectType();
this.x = Math.random() * canvas.width;
this.y = -20;
this.size = this.getSize();
this.speed = Math.random() * 2 + 0.5;
this.drift = Math.random() * 1.5 - 0.75;
this.opacity = Math.random() * 0.4 + 0.6;
this.rotationSpeed = (Math.random() - 0.5) * 0.05;
this.swingAmplitude = Math.random() * 1 + 0.5;
this.swingSpeed = Math.random() * 0.02 + 0.01;
this.swingOffset = Math.random() * Math.PI * 2;
this.color = this.getColor();
this.pulseSpeed = Math.random() * 0.05 + 0.02;
this.pulseOffset = Math.random() * Math.PI * 2;
}
getSize() {
let baseSize;
switch (this.type) {
case "snowflake":
baseSize = Math.random() * 2 + 1;
break;
case "star":
baseSize = Math.random() * 4 + 3;
break;
case "gift":
baseSize = Math.random() * 6 + 4;
break;
case "sparkle":
baseSize = Math.random() * 2 + 1;
break;
default:
baseSize = 2;
}
return baseSize * settings.sizeMultiplier;
}
getColor() {
switch (this.type) {
case "snowflake":
return "#ffffff";
case "star":
return ["#FFD700", "#FFA500", "#FFFF00"][
Math.floor(Math.random() * 3)
];
case "gift":
return ["#FF0000", "#00FF00", "#0000FF", "#FFD700"][
Math.floor(Math.random() * 4)
];
case "sparkle":
return ["#FF69B4", "#00CED1", "#FFD700", "#FF1493"][
Math.floor(Math.random() * 4)
];
default:
return "#ffffff";
}
}
update() {
this.x +=
Math.sin(this.y * this.swingSpeed + this.swingOffset) *
this.swingAmplitude *
0.1;
this.y += this.speed;
this.x += this.drift;
this.rotation += this.rotationSpeed;
if (this.y > canvas.height + 20) {
this.reset();
}
if (this.x > canvas.width + 20) {
this.x = -20;
} else if (this.x < -20) {
this.x = canvas.width + 20;
}
}
draw() {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
if (settings.enablePulse) {
const pulse =
Math.sin(Date.now() * this.pulseSpeed + this.pulseOffset) * 0.2 +
0.8;
ctx.globalAlpha = this.opacity * pulse;
} else {
ctx.globalAlpha = this.opacity;
}
switch (this.type) {
case "snowflake":
this.drawSnowflake();
break;
case "star":
this.drawStar();
break;
case "gift":
this.drawGift();
break;
case "sparkle":
this.drawSparkle();
break;
}
ctx.restore();
}
drawSnowflake() {
ctx.fillStyle = this.color;
ctx.shadowBlur = 3;
ctx.shadowColor = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = this.opacity * 0.3;
ctx.beginPath();
ctx.arc(0, 0, this.size * 1.3, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = this.opacity;
ctx.shadowBlur = 0;
}
drawStar() {
ctx.fillStyle = this.color;
ctx.shadowBlur = 10;
ctx.shadowColor = this.color;
ctx.beginPath();
for (let i = 0; i < 5; i++) {
const angle = (i * 4 * Math.PI) / 5 - Math.PI / 2;
const x = Math.cos(angle) * this.size;
const y = Math.sin(angle) * this.size;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fill();
ctx.shadowBlur = 0;
}
drawGift() {
ctx.shadowBlur = 5;
ctx.shadowColor = "rgba(0,0,0,0.3)";
ctx.fillStyle = this.color;
ctx.fillRect(-this.size / 2, -this.size / 2, this.size, this.size);
ctx.fillStyle = "#FFD700";
ctx.fillRect(
-this.size / 2,
-this.size / 8,
this.size,
this.size / 4
);
ctx.fillRect(
-this.size / 8,
-this.size / 2,
this.size / 4,
this.size
);
ctx.beginPath();
ctx.arc(-this.size / 4, -this.size / 2, this.size / 4, 0, Math.PI * 2);
ctx.arc(this.size / 4, -this.size / 2, this.size / 4, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
}
drawSparkle() {
ctx.shadowBlur = 8;
ctx.shadowColor = this.color;
for (let i = 0; i < 4; i++) {
ctx.save();
ctx.rotate((Math.PI / 4) * i);
ctx.beginPath();
ctx.moveTo(0, -this.size * 0.7);
ctx.lineTo(0, this.size * 0.7);
ctx.lineWidth = this.size / 3;
ctx.strokeStyle = this.color;
ctx.stroke();
ctx.restore();
}
const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, this.size);
gradient.addColorStop(0, this.color);
gradient.addColorStop(1, "rgba(255,255,255,0)");
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(0, 0, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
}
}
// Parçacıkları oluştur
for (let i = 0; i < settings.particleCount; i++) {
particles.push(new Particle());
}
// Animasyon döngüsü
let lastFrameTime = Date.now();
const targetFPS = isMobile ? 30 : 60;
const frameInterval = 1000 / targetFPS;
function animate() {
if (isPaused || !isPageVisible) return;
const now = Date.now();
const elapsed = now - lastFrameTime;
if (elapsed > frameInterval) {
lastFrameTime = now - (elapsed % frameInterval);
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach((particle) => {
particle.update();
particle.draw();
});
}
animationId = requestAnimationFrame(animate);
}
// Sayfa yüklendikten sonra animasyonu başlat
if (document.readyState === "complete") {
setTimeout(() => animate(), 100);
} else {
window.addEventListener("load", () => {
setTimeout(() => animate(), 100);
});
}
// Dışarıya kontrol fonksiyonları
return {
stop: function () {
isPaused = true;
cancelAnimationFrame(animationId);
canvas.remove();
window.removeEventListener("resize", resizeCanvas);
document.removeEventListener("visibilitychange", handleVisibilityChange);
particles.length = 0;
particles = null;
},
pause: function () {
isPaused = true;
cancelAnimationFrame(animationId);
},
resume: function () {
if (!isPaused) return;
isPaused = false;
if (isPageVisible) {
animate();
}
},
updateSettings: function (newOptions) {
Object.assign(settings, newOptions);
if (
newOptions.particleCount !== undefined ||
newOptions.partikülSayısı !== undefined
) {
const newCount = newOptions.partikülSayısı || newOptions.particleCount;
particles.length = 0;
for (let i = 0; i < newCount; i++) {
particles.push(new Particle());
}
}
},
};
}
// 🎉 Sayfa yüklendiğinde efekti otomatik başlat
window.addEventListener("DOMContentLoaded", function () {
createChristmasSnowEffect({
partikülSayısı: 80, // Parçacık sayısı
yıldızlarıAktifEt: false, // Yıldızlar
hediyeleriAktifEt: false, // Hediye kutuları
ışıltılarıAktifEt: false, // Işıltı partikülleri
parlamaEfekti: false, // Nefes alan parlama efekti
partikülBoyutu: 1.5, // 1 = normal, 1.5 = %50 büyük
hareketAzaltmaTercihi: true // Erişilebilirlik: true bırak
});
});
</script>Kaydettiğinizde script aktif olur ve mağazanızı yenilediğinizde kar efektini görmeye başlarsınız.
3. Adım: Efekti Kendi Kampanyanıza Göre Özelleştirin
Scriptin en altındaki createChristmasSnowEffect({...}) kısmındaki parametreleri değiştirerek görünümü kolayca özelleştirebilirsiniz:
createChristmasSnowEffect({
partikülSayısı: 60, // Mobilde 30-40, masaüstünde 60-100 ideal
yıldızlarıAktifEt: true, // true → yıldızları aç
hediyeleriAktifEt: false, // true → hediye kutuları da yağsın
ışıltılarıAktifEt: false, // true → pembe/mavi ışıltılar
parlamaEfekti: false, // true → hafif pulse/parlama
partikülBoyutu: 1.2, // 0.8 küçük, 1.5 daha büyük
hareketAzaltmaTercihi: true
});Performans ve Erişilebilirlik Avantajları
- Kullanıcı başka sekmeye geçtiğinde animasyon otomatik olarak durur, geri döndüğünde devam eder.
prefers-reduced-motionaktif olan kullanıcılar için efekt hiç başlamaz; bu da modern erişilebilirlik standartlarıyla uyumludur.- Mobilde daha düşük FPS ve daha az parçacık kullanılarak performans korunur.



