<!--
I created an advanced critterpedia for Animal Crossing New Horizons.
And this is a PoC about the fish shadow preview I built inside the fish detail page.
To see the whole app, please visit:
http://critterpedia-plus.mutoo.im/
-->
<div id="canvas" />
html, body {
width: 100%;
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
#canvas {
border-radius: 50px;
overflow: hidden;
}
View Compiled
const fishShadowImg =
"https://raw.githubusercontent.com/mutoo/critterpedia-plus/master/app/assets/images/fish-shadow.png";
const fishShadowImgWithFin =
"https://raw.githubusercontent.com/mutoo/critterpedia-plus/master/app/assets/images/fish-shadow-fin.png";
const waterImg =
"https://raw.githubusercontent.com/mutoo/critterpedia-plus/master/app/assets/images/water.png";
const app = new PIXI.Application({ width: 300, height: 300 });
const canvas = document.getElementById("canvas");
canvas.appendChild(app.view);
const gui = new dat.GUI({
load: {
preset: "Default",
remembered: {
Default: { "0": {} },
"smallest (1)": {
"0": {
segment: 20,
length: 30,
fin: false,
thickness: 12,
curvedness: 0.3,
amp: 5
}
},
"small (2)": {
"0": {
segment: 20,
length: 50,
fin: false,
thickness: 22,
curvedness: 0.3,
amp: 5
}
},
"medium (3)": {
"0": {
segment: 20,
length: 70,
fin: false,
thickness: 30,
curvedness: 0.2,
amp: 10
}
},
"medium (4)": {
"0": {
segment: 20,
length: 90,
fin: false,
thickness: 35,
curvedness: 0.2,
amp: 10
}
},
"large (5)": {
"0": {
segment: 20,
length: 100,
fin: false,
thickness: 40,
curvedness: 0.2,
amp: 10
}
},
"largest (6)": {
"0": {
segment: 20,
length: 120,
fin: false,
thickness: 40,
curvedness: 0.35,
amp: 10
}
},
"largest (6) with fin": {
"0": {
segment: 20,
length: 120,
fin: true,
thickness: 40,
curvedness: 0.35,
amp: 10
}
},
narrow: {
"0": {
segment: 20,
length: 130,
fin: false,
thickness: 20,
curvedness: 2,
amp: 10
}
}
},
closed: false,
folders: {}
}
});
const config = {
segment: 20,
thickness: 40,
curvedness: 0.35,
amp: 10,
length: 120,
fin: true
};
const segmentCtrl = gui.add(config, "segment", 3, 50);
const lenCtrl = gui.add(config, "length", 20, 150);
const finCtrl = gui.add(config, "fin");
const thickCtrl = gui.add(config, "thickness", 10, 50);
gui.add(config, "curvedness", 0.1, 2);
gui.add(config, "amp", 1, 30);
gui.remember(config);
const water = PIXI.Sprite.from(waterImg);
water.anchor.set(0.5);
water.x = app.screen.width / 2;
water.y = app.screen.height / 2;
app.stage.addChild(water);
let fishShadow;
let fishShadowPts;
function setupFishShadow() {
if (fishShadow) app.stage.removeChild(fishShadow);
fishShadowPts = [];
for (let i = 0; i < config.segment; i++) {
fishShadowPts.push(
new PIXI.Point((i / config.segment) * config.length, 0)
);
}
fishShadow = new PIXI.SimpleRope(
PIXI.Texture.from(config.fin ? fishShadowImgWithFin : fishShadowImg),
fishShadowPts
);
fishShadow.scale.y = config.thickness / 30;
fishShadow.x = app.screen.width / 2 - config.length / 2;
fishShadow.y = app.screen.height / 2;
app.stage.addChild(fishShadow);
}
setupFishShadow();
segmentCtrl.onFinishChange((val) => setupFishShadow());
lenCtrl.onFinishChange((val) => setupFishShadow());
finCtrl.onFinishChange((val) => setupFishShadow());
thickCtrl.onFinishChange((val) => setupFishShadow());
let timelapse = 0;
app.ticker.add((delta) => {
timelapse += delta;
fishShadowPts?.forEach((p, idx) => {
// the fish tail animail composed by two basic functions:
// https://www.desmos.com/calculator/0cmkc1w148
const i = idx / config.segment;
p.y =
i ** 2 *
Math.sin(timelapse / 10 - i * 2 * Math.PI * config.curvedness) *
config.amp;
});
});
View Compiled
This Pen doesn't use any external CSS resources.