MediaWiki:Gadget-ShroomScript.js
From the Super Mario Wiki, the Mario encyclopedia
Jump to navigationJump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/* General-purpose JavaScript used for The 'Shroom */
const ShroomUtil = {
inAndOut: function inAndOut(t) {
return 3 * t * t - 2 * t * t * t;
},
// https://stackoverflow.com/a/36481059
gaussianRandom: function gaussianRandom(mean, stdev) {
const u = 1 - Math.random();
const v = Math.random();
const z = Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
return z * (stdev || 1) + (mean || 0);
},
};
/*
Creates a card flipping animation.
*/
var cards = document.getElementsByClassName('shroomCardInner');
for (var cardIdx = 0; cardIdx < cards.length; cardIdx++) {
(function (index) {
cards[index].addEventListener('click', function () {
cards[index].classList.toggle('active');
});
})(cardIdx);
}
// END OF CARD FLIP SEGMENT
/*
* Slideshow effect, but you can put whatever you want instead of just an image.
*/
function plusSlides(n, slideShow) {
changeSlides((slideShow.dataset.slideidx = parseInt(slideShow.dataset.slideidx) + n), slideShow);
}
function changeSlides(n, slideShow) {
var slides = slideShow.querySelectorAll('.shroomSlideshowSlide');
var tabs = slideShow.querySelectorAll('.shroomSlideshowTab');
var indexText = slideShow.querySelector('.shroomSlideshowIndex');
if (n > slides.length) {
slideShow.dataset.slideidx = 1;
}
if (n < 1) {
slideShow.dataset.slideidx = slides.length;
}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = 'none';
}
for (i = 0; i < tabs.length; i++) {
tabs[i].classList.remove('active');
}
slides[slideShow.dataset.slideidx - 1].style.display = 'block';
tabs[slideShow.dataset.slideidx - 1].classList.add('active');
indexText.innerHTML = slideShow.dataset.slideidx + ' / ' + slides.length;
}
var slideShows = document.getElementsByClassName('shroomSlideshow');
for (var slideshowIdx = 0; slideshowIdx < slideShows.length; slideshowIdx++) {
changeSlides(slideShows[slideshowIdx].dataset.slideidx, slideShows[slideshowIdx]);
}
var leftArrows = document.getElementsByClassName('shroomSlideshowLeft');
for (var leftIdx = 0; leftIdx < leftArrows.length; leftIdx++) {
leftArrows[leftIdx].addEventListener('click', function () {
var slideShow = this.closest('.shroomSlideshow');
plusSlides(-1, slideShow);
});
}
var rightArrows = document.getElementsByClassName('shroomSlideshowRight');
for (var rightIdx = 0; rightIdx < rightArrows.length; rightIdx++) {
rightArrows[rightIdx].addEventListener('click', function () {
var slideShow = this.closest('.shroomSlideshow');
plusSlides(1, slideShow);
});
}
var tabs = document.getElementsByClassName('shroomSlideshowTab');
for (var tabsIdx = 0; tabsIdx < tabs.length; tabsIdx++) {
tabs[tabsIdx].addEventListener('click', function () {
var slideShow = this.closest('.shroomSlideshow');
var index = parseInt(this.dataset.index);
changeSlides(slideShow.dataset.slideidx = index, slideShow);
});
}
// END OF SLIDESHOW EFFECT
/*
* Archives
*/
var showAll = document.getElementById('shroomArchiveCollapseAll');
if (showAll) {
showAll.addEventListener('click', function () {
var text = showAll.innerText;
showAll.innerText = text === 'Expand all' ? 'Collapse all' : 'Expand all';
document.querySelectorAll('.shroom-collapsible.mw-collapsible').forEach(function (item) {
var expandText = item.dataset.expandtext, collapseText = item.dataset.collapsetext;
var toggle = item.querySelector('a.mw-collapsible-text');
if ((text === 'Expand all' && toggle.innerText === expandText) ||
(text === 'Collapse all' && toggle.innerText === collapseText)) {
toggle.click();
}
});
});
}
/*
* Special Issue 195: dynamic curtain
*/
function ShroomCurtain(container) {
this.container = container;
this.canvas = document.createElement('canvas');
this.button = document.createElement('button');
this.ctx = this.canvas.getContext('2d');
this.raf = this.renderCurtains.bind(this);
this.oc = this.openCurtain.bind(this);
this.canvas.className = 'shroom195curtain-canvas';
this.button.className = 'shroom195curtain-button';
this.button.textContent = 'Open the curtains';
var ct = localStorage.getItem('shroom_195curtain');
var execute = ct ? parseInt(ct) + 30 * 60 * 1000 < Date.now() : true;
if (execute) {
this.container.insertBefore(this.button, this.container.firstChild);
this.container.insertBefore(this.canvas, this.container.firstChild);
requestAnimationFrame(this.raf);
this.button.addEventListener('click', this.oc);
window.addEventListener('resize', this.setCanvasSize.bind(this));
this.setCanvasSize();
}
var placeholder = document.querySelector('.shroom195curtain-placeholder');
placeholder.classList.add('exit');
setTimeout(function () {
return placeholder.parentNode.removeChild(placeholder);
}, 1000);
}
ShroomCurtain.prototype.openCurtain = function openCurtain() {
var _this = this;
this.opening = Date.now();
this.button.removeEventListener('click', this.oc);
this.button.classList.add('exit');
setTimeout(function () {
return _this.button.parentNode.removeChild(_this.button);
}, 1000);
localStorage.setItem('shroom_195curtain', Date.now().toString());
};
ShroomCurtain.prototype.setCanvasSize = function setCanvasSize() {
var containerSize = this.container.getBoundingClientRect();
this.canvas.width = Math.round(containerSize.width * window.devicePixelRatio);
this.canvas.height = Math.round(containerSize.height * window.devicePixelRatio);
this.canvas.style.width = ''.concat(containerSize.width, 'px');
this.canvas.style.height = ''.concat(containerSize.height, 'px');
};
ShroomCurtain.prototype.renderCurtains = function renderCurtains(timestamp) {
this.ctx.resetTransform();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
var openingDuration = this.opening ? Date.now() - this.opening : 0;
var openingProgress = openingDuration / (this.canvas.width / window.devicePixelRatio * 4 + 1000);
this.ctx.fillStyle = 'rgba(0,0,0,'.concat(1 - ShroomUtil.inAndOut(Math.min(openingProgress, 1)), ')');
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.renderCurtain(timestamp, true, openingDuration);
this.renderCurtain(timestamp, false, openingDuration);
if (openingProgress <= 1) requestAnimationFrame(this.raf); else this.container.removeChild(this.canvas);
};
ShroomCurtain.prototype.renderCurtain = function renderCurtain(timestamp, left, openingDuration) {
var m = left ? -1 : 1;
var neededFolds = Math.max(Math.round(this.canvas.width / window.devicePixelRatio / 90), 2);
var foldWidth = this.canvas.width / neededFolds / 2;
var curtainOffset = Math.pow(Math.min(openingDuration / (this.canvas.width / window.devicePixelRatio * 4 + 1000), 1), 2) * this.canvas.width / 2;
var curtainSkewOffset = (ShroomUtil.inAndOut(Math.min(openingDuration / 3000, 1)) * m * -150 + Math.sin(timestamp / 1000) * (foldWidth / 6)) * window.devicePixelRatio;
var gr = this.ctx.createLinearGradient(left ? this.canvas.width : 0, 0, this.canvas.width / 2, 0);
gr.addColorStop(0, '#900');
gr.addColorStop(.95, '#c00');
gr.addColorStop(1, '#a00');
this.ctx.fillStyle = gr;
this.ctx.save();
this.ctx.beginPath();
this.ctx.moveTo(left ? this.canvas.width : 0, 0);
this.ctx.lineTo(this.canvas.width / 2 + m * (1 - curtainOffset), 0);
this.ctx.lineTo(this.canvas.width / 2 + curtainSkewOffset + m * (1 - curtainOffset), this.canvas.height);
this.ctx.lineTo(left ? this.canvas.width : 0, this.canvas.height);
this.ctx.closePath();
this.ctx.fill();
this.ctx.clip();
for (var i = 0; i < neededFolds; i++) {
var foldX = left ? this.canvas.width - foldWidth * (i + 1) : foldWidth * i;
var slideOffset = curtainOffset / ((neededFolds - i - 1) / (neededFolds - 1) + 1) * m;
var subtractSway = -Math.tan(curtainSkewOffset / this.canvas.height) * (i / neededFolds);
var foldGr = this.ctx.createLinearGradient(foldX - slideOffset, 0, foldX + foldWidth - slideOffset, 0);
for (var grI = 0; grI <= 1; grI += .1)
// cos((x - .5) * PI * 2) + 1
foldGr.addColorStop(grI, 'rgba(0,0,0,'.concat((Math.cos((grI - .5) * Math.PI * 2) + 1) * .1, ')'));
this.ctx.setTransform(1, 0, Math.tan(foldWidth / 3 / this.canvas.height) * Math.cos(timestamp / (1000 + Math.cos(foldX) * 100) + Math.cos(foldX * 1.2)) - subtractSway, 1, 0, 0);
this.ctx.fillStyle = foldGr;
this.ctx.fillRect(foldX - slideOffset, 0, foldWidth, this.canvas.height);
}
this.ctx.restore();
};
var curtainContainer = document.querySelector('.shroom195curtain');
if (curtainContainer) {
new ShroomCurtain(curtainContainer);
}
/*
* Special Issue 200
*/
// Fireworks
function ShroomFireworks(container, debug) {
this.debug = debug || false;
const _this = this;
this.varOff = 0.08;
this.gravity = 0.6;
this.radius = 1.5;
this.particles = [];
this.container = container;
this.drawCanvas = document.createElement('canvas');
this.drawCtx = this.drawCanvas.getContext('2d');
this.setWorker();
container.querySelectorAll('.fireworks-images img').forEach(function (img) {
img.crossOrigin = 'anonymous';
if (img.complete) _this.addSpritesheet(img);
else img.onload = function () {
_this.addSpritesheet(img);
};
});
container.append(this.drawCanvas);
this.setCanvasConfig();
const observer = new ResizeObserver(this.setCanvasConfig.bind(this));
observer.observe(container);
requestAnimationFrame(this.render.bind(this));
}
ShroomFireworks.prototype.setCanvasConfig = function setCanvasConfig() {
const width = this.container.clientWidth, height = Math.min(this.container.clientHeight, window.innerHeight);
const f = width < 640 || height < 640 ? 1.5 : 1;
this.width = width * f;
this.height = height * f;
this.drawCanvas.width = width * window.devicePixelRatio;
this.drawCanvas.height = height * window.devicePixelRatio;
this.drawCanvas.style.width = width + 'px';
this.drawCanvas.style.height = height + 'px';
this.drawCtx.resetTransform();
this.drawCtx.scale(window.devicePixelRatio / f, window.devicePixelRatio / f);
this.drawCtx.globalCompositeOperation = 'lighter';
};
ShroomFireworks.prototype.setWorker = function setWorker() {
this.worker = new Worker(URL.createObjectURL(new Blob([
'const images = [];',
'const calcCanvas = new OffscreenCanvas(256,256);',
'const calcCtx = calcCanvas.getContext("2d", {willReadFrequently: true});',
'self.onmessage = async(event)=>{',
' if (event.data.ib) {',
' const ib = event.data.ib;',
' const sprites = Math.floor(ib.width / ib.height);',
' for (let i = 0; i < sprites; i++) {',
' const sprite = await createImageBitmap(ib, ib.height * i, 0, ib.height, ib.height);',
' images.push(sprite);',
' }',
' }',
' if (event.data.rf) {',
' calculate();',
' }',
'};',
'function calculate() {',
' if (!images.length)',
' return;',
' const index = images.length > 1 ? Math.floor(Math.random() * (images.length - 1)) : 0;',
' const img = images[index];',
' images.splice(index, 1);',
' images.push(img);',
' calcCtx.clearRect(0, 0, calcCanvas.width, calcCanvas.height);',
' calcCtx.drawImage(img, 0, 0, calcCanvas.width, calcCanvas.height);',
' const calculatedParticles = [];',
' calculatedParticles.push({',
' ttl: 2e3,',
' speed: calcCanvas.width / 2,',
' img',
' });',
' for (let i = 0; i < 5e3; i++) {',
' const x = Math.floor(Math.random() * calcCanvas.width);',
' const y = Math.floor(Math.random() * calcCanvas.height);',
' const data = calcCtx.getImageData(x, y, 1, 1);',
' if (data.data[3] < 30)',
' continue;',
' calculatedParticles.push({',
' r: data.data[0],',
' g: data.data[1],',
' b: data.data[2],',
' speedX: x - calcCanvas.width / 2,',
' speedY: y - calcCanvas.width / 2,',
' ttl: gaussianRandom(3e3, 500)',
' });',
' if (calculatedParticles.length >= 400)',
' break;',
' }',
' self.postMessage({',
' f: calculatedParticles',
' });',
'}',
ShroomUtil.gaussianRandom.toString(),
])));
const _this = this;
this.worker.onmessage = function (event) {
if (event.data.f) _this.summonFireworks(event.data.f);
};
};
ShroomFireworks.prototype.addSpritesheet = function addSpritesheet(img) {
const _this = this;
createImageBitmap(img).then(function (ib) {
_this.worker.postMessage({ib: ib});
});
};
ShroomFireworks.prototype.spawnFireworks = function spawnFireworks() {
clearTimeout(this.spawnTimeout);
this.spawnTimeout = setTimeout(this.spawnFireworks.bind(this), Math.min(ShroomUtil.gaussianRandom(1500, 1e3), 4e3));
if (this.particles.length >= 300) return;
this.worker.postMessage({rf: true});
};
ShroomFireworks.prototype.summonFireworks = function summonFireworks(calculatedParticles) {
if (this.particles.length >= 300) return;
const spawnX = ShroomUtil.gaussianRandom(this.width / 2, this.width / 4);
const spawnY = ShroomUtil.gaussianRandom(this.height / 2, this.height / 6);
const speedFactor = ShroomUtil.gaussianRandom(0.8, 0.2);
const _this = this;
calculatedParticles.forEach(function (cp) {
'speed' in cp ? _this.particles.push({
x: spawnX,
y: spawnY,
speed: cp.speed * speedFactor,
addY: _this.gravity,
ttl: cp.ttl,
img: cp.img,
birth: performance.now(),
scale: 0,
}) : _this.particles.push({
r: cp.r,
g: cp.g,
b: cp.b,
x: spawnX,
y: spawnY,
speedX: cp.speedX * speedFactor,
speedY: cp.speedY * speedFactor,
addX: Math.random() * _this.varOff - _this.varOff / 2,
addY: Math.random() * _this.varOff - _this.varOff / 2 + _this.gravity,
ttl: cp.ttl,
birth: performance.now(),
});
});
};
ShroomFireworks.prototype.stopSpawnFireworks = function stopSpawnFireworks() {
clearTimeout(this.spawnTimeout);
};
ShroomFireworks.prototype.render = function render(ts) {
requestAnimationFrame(this.render.bind(this));
const start = this.debug && performance.now();
this.drawCtx.clearRect(0, 0, this.width, this.height);
for (var i = 0; i < this.particles.length; i++) {
const p = this.particles[i];
const tl = -ts + p.birth + p.ttl;
if (tl <= 0) {
this.particles.splice(i, 1);
--i;
continue;
}
if ('img' in p) {
this.drawCtx.globalAlpha = tl > 2000 ? 0.7 : ShroomUtil.inAndOut(tl / 2000) * 0.7;
this.drawCtx.drawImage(p.img, p.x - p.scale / 2, p.y - p.scale / 2, p.scale, p.scale);
p.y += p.addY;
p.scale += p.speed / 7.5;
p.speed = p.speed * 0.98;
} else {
this.drawCtx.fillStyle = 'rgb(' + p.r + ' ' + p.g + ' ' + p.b + ')';
this.drawCtx.globalAlpha = 1;
this.drawCtx.beginPath();
this.drawCtx.arc(p.x, p.y, this.radius * (tl > 1000 ? 1 : ShroomUtil.inAndOut(tl / 1000)), 0, 2 * Math.PI);
this.drawCtx.closePath();
this.drawCtx.fill();
this.drawCtx.globalAlpha = 0.1;
this.drawCtx.beginPath();
this.drawCtx.arc(p.x, p.y, this.radius * (tl > 1000 ? 5 : 5 * ShroomUtil.inAndOut(tl / 1000)), 0, 2 * Math.PI);
this.drawCtx.closePath();
this.drawCtx.fill();
p.x += p.speedX / 15 + p.addX;
p.y += p.speedY / 15 + p.addY;
p.speedX = p.speedX * 0.98;
p.speedY = p.speedY * 0.98;
}
}
if (this.debug) {
const end = performance.now();
this.drawCtx.fillStyle = '#fff';
this.drawCtx.globalAlpha = 1;
this.drawCtx.fillText(this.particles.length + ' particles ' + (end - start).toFixed(1) + 'ms', 10, 20);
}
};
const fireworksContainer = document.querySelector('.shroom-fireworks');
if (fireworksContainer) {
const fireworks = new ShroomFireworks(fireworksContainer);
const logo200container = document.getElementById('shroom200logo');
if (logo200container) {
const startBtn = document.createElement('button');
const stopBtn = document.createElement('button');
startBtn.className = 'start';
stopBtn.className = 'stop';
startBtn.innerText = 'Click here to start the show';
stopBtn.innerText = '× Stop';
logo200container.append(startBtn, stopBtn);
var animTimeout;
startBtn.addEventListener('click', function () {
clearTimeout(animTimeout);
logo200container.classList.add('transition');
animTimeout = setTimeout(function () {
logo200container.classList.add('opened');
logo200container.classList.remove('transition');
fireworks.spawnFireworks();
}, 750);
});
stopBtn.addEventListener('click', function () {
fireworks.stopSpawnFireworks();
clearTimeout(animTimeout);
logo200container.classList.add('transition');
animTimeout = setTimeout(function () {
logo200container.classList.remove('opened');
logo200container.classList.remove('transition');
}, 750);
});
} else {
fireworks.spawnFireworks();
}
}
// /fireworks
// Vending machine
var vendingMachine = document.getElementById('vending-machine-container');
if (vendingMachine) {
var itemContainer = document.getElementById('vending-machine-items');
itemContainer.classList.add('hidden');
var items = document.querySelectorAll('#vending-machine-items li');
var button = document.createElement('button');
button.className = 'vending-machine';
vendingMachine.querySelector('.center').prepend(button);
button.append(vendingMachine.querySelector('.floatnone'));
var closeButton = document.createElement('button');
closeButton.className = 'close';
closeButton.disabled = true;
closeButton.textContent = '×';
itemContainer.append(closeButton);
button.addEventListener('click', function () {
button.disabled = true;
closeButton.disabled = false;
var selected = Math.floor(Math.random() * items.length);
for (var i = 0; i < items.length; i++)
items[i].style.display = selected === i ? '' : 'none';
itemContainer.classList.remove('hidden');
closeButton.focus();
});
closeButton.addEventListener('click', function () {
button.disabled = false;
closeButton.disabled = true;
itemContainer.classList.add('hidden');
button.focus();
});
setTimeout(function () {
vendingMachine.classList.add('initialised');
}, 1);
}
// /vending machine