色々気になった
テーマ変更後のチェックをしていて……気になってきた画面のエフェクト
| ライトモード | ダークモード | |
|---|---|---|
| 画面クリック | シャボン玉ふわり | シャボン玉ふわり |
| 背景 | 時々流れ星ヒュン |
ダークモードを夜空に見立てて 時々キラッヒュンと流れ星
星空とか流星群とかにしないで あえて抑えた感じにしていたけど……物足りない?
夜空にもシャボン玉
きれいだったけど……僕のシャボン玉のイメージは青空の下?
ダークモードを変えてみた
最終的にはこんな感じ
| ライトモード | ダークモード | |
|---|---|---|
| 画面クリック | シャボン玉ふわり | スカイランタンふわり |
| 背景 | スカイランタンふわりふわり |
ライトモードは そのままシャボン玉
ダークモードを 流れ星とシャボン玉の代わりに スカイランタンに
イメージはディズニー映画 塔の上のラプンツェル そのランタンフェスティバル
流石に映画みたいな数のランタンだと重くなるので……今は最多34個かな? でも雰囲気出てるー
うーん……良き(˶ᵔ ᵕ ᵔ˶)
コードも載せてみました
もしよろしければどうぞ くれぐれもバックアップはお忘れなく
(NOTEにまとめてありますが)
CSS → ランタンの見た目
BG JS → 背景ランタン
TAP JS → クリック演出
ちなみに シャボン玉のCSSはほぼ無くした らしい
- 変更前のシャボン玉(CSS/DOM版)
.bubble を作って
* ガラスっぽい縁
* backdrop-filter: blur(4px) の屈折感
* mix-blend-mode: screen の発光
* ::after の白い反射
* .particle の破裂
まで付いてた かなり装飾が多い豪華版
- 変更後のシャボン玉(Canvas版)
Canvas で
* 円を描く
* カラフルな radial-gradient
* 外周stroke
* ふわっと上昇
* 透明フェード破裂パーティクルは無い
白い反射も無い
ガラスの屈折感 も弱いでも見た目は幻想的 / 軽い / 滑らか
下記のMulti-tabs Code Blockは「drK」
(BG JSの16行目付近)
(TAP JSの37行目付近)
/* =========================
🏮 Sky Lantern Pack
Rapunzel Night ver.
========================= */
.sky-lantern,
.hero-lantern{
position: fixed;
left: 0;
top: 0;
pointer-events: none;
opacity: 0;
transform-origin: center center;
will-change: transform, opacity;
overflow: visible;
}
/* 背景 */
.sky-lantern{
z-index: 999999;
animation: lanternFloat linear forwards;
/* 背景だけ少し濃いぽわ */
box-shadow:
0 0 16px rgba(255,215,120,.16),
0 0 40px rgba(255,170,70,.10);
}
/* 主役 */
.hero-lantern{
z-index: 1000000;
width: 32px;
height: 44px;
animation: heroLanternFloat 12s ease-in-out forwards;
}
/* ===== 縦長 ===== */
.sky-lantern.tall,
.hero-lantern.tall{
border-radius: 16% 16% 12% 12% / 8% 8% 10% 10%;
}
/* ===== 平たい ===== */
.sky-lantern.flat,
.hero-lantern.flat{
border-radius: 20% 20% 14% 14% / 12% 12% 10% 10%;
}
/* ===== 真円 + 底カット ===== */
.sky-lantern.round,
.hero-lantern.round{
width: 100%;
aspect-ratio: 1 / 1;
border-radius: 50%;
}
/* 本体 */
.sky-lantern::before,
.hero-lantern::before{
content:"";
position:absolute;
inset:0;
border-radius:inherit;
background:
linear-gradient(
to bottom,
rgba(182,95,28,.62) 0%,
rgba(226,132,46,.74) 34%,
rgba(255,192,92,.88) 68%,
rgba(255,236,170,.98) 100%
);
box-shadow:
0 0 10px rgba(255,210,130,.20),
0 0 28px rgba(255,170,70,.12),
0 0 70px rgba(255,140,50,.06);
filter: blur(.2px);
}
/* 底面リング発光 */
.sky-lantern::after,
.hero-lantern::after{
content:"";
position:absolute;
left:12%;
right:12%;
bottom:4%;
height:16%;
border-radius:50%;
background:
radial-gradient(
ellipse at center,
rgba(255,255,235,.98) 0%,
rgba(255,236,160,.92) 28%,
rgba(255,180,70,.55) 58%,
rgba(255,120,40,0) 100%
);
box-shadow:
0 0 14px rgba(255,242,190,.34),
0 0 38px rgba(255,188,82,.18),
0 0 72px rgba(255,160,62,.08);
}
/* 縦長 / 平たい */
.sky-lantern.tall::before,
.hero-lantern.tall::before,
.sky-lantern.flat::before,
.hero-lantern.flat::before{
clip-path: polygon(
6% 0%,
94% 0%,
100% 82%,
86% 100%,
14% 100%,
0% 82%
);
}
/* 丸 */
.sky-lantern.round::before,
.hero-lantern.round::before{
clip-path: polygon(
0% 45%,
6% 18%,
18% 4%,
82% 4%,
94% 18%,
100% 45%,
96% 84%,
82% 100%,
18% 100%,
4% 84%
);
}
/* アニメーション */
@keyframes lanternFloat{
0%{
opacity:0;
transform:translate3d(0,0,0) scale(.75);
}
8%{
opacity:.96;
}
100%{
opacity:0;
transform:
translate3d(var(--dx),var(--dy),0)
scale(.20)
rotate(var(--rot));
}
}
@keyframes heroLanternFloat{
0%{
opacity:0;
transform:translate3d(0,0,0) scale(.9);
}
8%{
opacity:1;
}
100%{
opacity:0;
transform:
translate3d(var(--dx),var(--dy),0)
scale(.38)
rotate(var(--rot));
}
}
/* =========================
🏮 Sky Lantern Pack
Background Lantern JS
========================= */
<script>
/*<![CDATA[*/
(function(){
let timer = null;
let startedAt = 0;
let activeLanterns = 0;
let clusterX = null;
const container = document.body;
function isDarkMode(){
return document.documentElement.classList.contains("drK");
}
function rand(min, max){
return Math.random() * (max - min) + min;
}
/* 密集ポイント */
function spawnX(){
if (clusterX !== null && Math.random() < 0.72){
return Math.max(4, Math.min(96, clusterX + rand(-9, 9)));
}
return rand(4, 96);
}
function createLantern(){
if (!isDarkMode()) return;
const el = document.createElement("div");
/* 四角70 / 丸30 */
if (Math.random() < 0.7){
el.className = "sky-lantern tall";
} else {
el.className = "sky-lantern round";
}
/* 位置 */
const x = spawnX();
const y = window.innerHeight + rand(40, 200);
el.style.left = x + "vw";
el.style.top = y + "px";
/* 小型化 */
const size = rand(12, 22);
el.style.width = size + "px";
if (el.classList.contains("round")) {
/* 完全な球体 */
el.style.height = size + "px";
} else {
/* 縦長 */
el.style.height = (size * 1.35) + "px";
}
/* ゆらぎ */
el.style.setProperty("--dx", rand(-90, 90) + "px");
el.style.setProperty("--dy", -(window.innerHeight + rand(280, 760)) + "px");
el.style.setProperty("--rot", rand(-5, 5) + "deg");
/* ゆっくり */
const duration = rand(26, 44);
el.style.animationDuration = duration + "s";
/* 色味C */
if (Math.random() < 0.2){
el.style.filter =
"hue-rotate(-10deg) saturate(.74) brightness(.92)";
} else {
el.style.filter =
"hue-rotate(" + rand(-6, 8) + "deg) saturate(1.06) brightness(1.04)";
}
container.appendChild(el);
activeLanterns++;
setTimeout(() => {
if (el.parentNode) el.remove();
activeLanterns = Math.max(0, activeLanterns - 1);
}, duration * 1000 + 100);
}
function loop(){
if (!isDarkMode()) return;
const elapsed = (Date.now() - startedAt) / 1000;
/* ゆっくり増える */
let target = 0;
if (elapsed < 6){
target = 0;
} else if (elapsed < 14){
target = 1;
} else if (elapsed < 24){
target = 2;
} else if (elapsed < 38){
target = 6;
} else if (elapsed < 55){
target = 14;
} else if (elapsed < 75){
target = 24;
} else {
target =
34 +
Math.floor(Math.sin(elapsed / 10) * 6) +
Math.floor(rand(-2, 6));
}
/* 密集ポイント更新 */
if (Math.random() < 0.10){
clusterX = rand(15, 85);
}
/* 補充 */
if (activeLanterns < target){
const add = Math.min(
target - activeLanterns,
1 + Math.floor(Math.random() * 3)
);
for (let i = 0; i < add; i++){
setTimeout(createLantern, i * 300);
}
}
/* まとまり上昇 */
if (elapsed > 55 && Math.random() < 0.20){
const burst = 2 + Math.floor(Math.random() * 4);
for (let i = 0; i < burst; i++){
setTimeout(createLantern, i * 240);
}
}
}
function startLanterns(){
if (timer) return;
startedAt = Date.now();
activeLanterns = 0;
clusterX = null;
timer = setInterval(loop, 1000);
}
function stopLanterns(){
if (!timer) return;
clearInterval(timer);
timer = null;
document
.querySelectorAll(".sky-lantern")
.forEach(el => el.remove());
activeLanterns = 0;
clusterX = null;
}
document.addEventListener("DOMContentLoaded", () => {
if (isDarkMode()) startLanterns();
});
const mo = new MutationObserver(() => {
if (isDarkMode()) startLanterns();
else stopLanterns();
});
mo.observe(document.documentElement, {
attributes: true,
attributeFilter: ["class"]
});
window.addEventListener("beforeunload", stopLanterns);
})();
/*]]>*/
</script>
/* =========================
🏮 Sky Lantern Pack
Tap Effect JS
Light = Bubble
Dark = Hero Lantern
========================= */
<script>
/*<![CDATA[*/
(function(){
/* ---- Bubble Canvas Setup ---- */
let bubbleCanvas = document.createElement("canvas");
bubbleCanvas.id = "bubble-canvas";
bubbleCanvas.style.position = "fixed";
bubbleCanvas.style.top = 0;
bubbleCanvas.style.left = 0;
bubbleCanvas.style.pointerEvents = "none";
bubbleCanvas.style.zIndex = 9999;
document.body.appendChild(bubbleCanvas);
let ctx = bubbleCanvas.getContext("2d");
let bubbles = [];
let w, h;
/* ---- Resize ---- */
function resizeCanvas(){
w = bubbleCanvas.width = window.innerWidth;
h = bubbleCanvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
/* ---- Dark Mode ---- */
function isDarkMode(){
return document.documentElement.classList.contains("drK");
}
/* ---- Bubble ---- */
function createBubble(x, y){
bubbles.push({
x,
y,
radius: 5 + Math.random() * 20,
opacity: 0.4 + Math.random() * 0.4,
speedX: -1 + Math.random() * 2,
speedY: -1 - Math.random() * 1.5,
life: 1
});
}
/* ---- Hero Lantern ---- */
function createHeroLantern(x, y){
const el = document.createElement("div");
el.className = "hero-lantern tall";
el.style.left = (x - 16) + "px";
el.style.top = (y - 22) + "px";
el.style.opacity = "0";
const dx = (-40 + Math.random() * 80);
const dy = -(window.innerHeight * 0.75 + Math.random() * 160);
const rot = (-6 + Math.random() * 12);
el.style.setProperty("--dx", dx + "px");
el.style.setProperty("--dy", dy + "px");
el.style.setProperty("--rot", rot + "deg");
document.body.appendChild(el);
/* 点灯 */
el.animate([
{
opacity: 0,
transform: "scale(.7)"
},
{
opacity: 1,
transform: "scale(1)"
}
], {
duration: 500,
easing: "ease-out",
fill: "forwards"
});
/* 0.5秒後に上昇 */
setTimeout(() => {
el.style.animationPlayState = "running";
el.classList.add("fly");
}, 500);
/* remove */
setTimeout(() => {
if (el.parentNode) el.remove();
}, 10000);
}
/* ---- Bubble Animation ---- */
function animate(){
ctx.clearRect(0, 0, w, h);
bubbles.forEach((b, i) => {
b.x += b.speedX;
b.y += b.speedY;
b.life -= 0.005;
if (b.life <= 0){
bubbles.splice(i, 1);
return;
}
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2);
ctx.strokeStyle = `rgba(173,216,230,${1.55 * b.life})`;
ctx.lineWidth = 2.8;
const hue = Math.floor(Math.random() * 360);
const gradient = ctx.createRadialGradient(
b.x - b.radius / 3,
b.y - b.radius / 3,
1,
b.x,
b.y,
b.radius
);
gradient.addColorStop(
0,
`hsla(${hue},90%,100%,${b.opacity})`
);
gradient.addColorStop(
0.4,
`hsla(${hue + 50},90%,70%,0.35)`
);
gradient.addColorStop(
0.7,
`hsla(${hue + 120},80%,60%,0.25)`
);
gradient.addColorStop(
1,
`rgba(173,216,230,0)`
);
ctx.fillStyle = gradient;
ctx.fill();
ctx.stroke();
});
requestAnimationFrame(animate);
}
animate();
/* ---- Click ---- */
window.addEventListener("click", (e) => {
/* dark */
if (isDarkMode()){
createHeroLantern(e.clientX, e.clientY);
return;
}
/* light */
createBubble(e.clientX, e.clientY);
const sound = document.getElementById("pop-sound");
if (sound){
sound.currentTime = 0;
sound.play().catch(() => {});
}
}, { passive:true });
})();
/*]]>*/
</script>
/* =========================
🏮 Sky Lantern Pack NOTE
========================= */
Mode:
- Light Mode = Bubble Canvas 🫧
- Dark Mode = Sky Lantern 🏮
Dark Switch:
- 判定 class = drK
- true → dark effect
- false → light effect
Install:
- CSS → <style> 内
- BG JS → </body> 前の script
- TAP JS → </body> 前の script
Customize:
[CSS]
- 背景ぽわ強さ
.sky-lantern → box-shadow
- 底の灯り位置
::after → bottom: 4%
- 色味
::before → linear-gradient
- 丸さ
.round / clip-path
[BG JS]
- 数
loop() の target
- 速度
duration = 26〜44s
- 密集感
spawnX()
clusterX
- 比率
tall 70%
round 30%
[TAP JS]
- 点灯待ち
500ms
- 主役サイズ
left/top 補正
hero size
Memo:
Safariが重くなる原因
↓
× large blur
× multiple drop-shadow
× huge glow stack
軽くて綺麗
↓
○ small shadow
○ thin glow
○ moderate count
