MediaWiki:Common.js: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
| Zeile 395: | Zeile 395: | ||
}); | }); | ||
| Zeile 941: | Zeile 734: | ||
m.content = 'light'; | m.content = 'light'; | ||
} | } | ||
}); | |||
// ------------------------------------- | |||
/* Whisky News – World of Whisky (Mannheim) – Popup (v1) | |||
- 1x/Tag je Nutzer | |||
- Canvas-Animation: Whisky-Glas (Wellen + Bläschen) | |||
- Zwei Bildkarten mit Links zu den Abfüllungen | |||
*/ | |||
mw.loader.using(['mediawiki.util','jquery']).then(function(){ | |||
(function($, mw){ | |||
'use strict'; | |||
var CONFIG = { | |||
enabled: true, | |||
id: 'wow_mannheim_whisky_news_v1', // bei Änderungen hochzählen | |||
title: 'Whisky News: Messeabfüllungen – World of Whisky (Mannheim)', | |||
introHTML: '<p>Frisch zur Messe in Mannheim: Zwei limitierte Abfüllungen. Schau sie dir an und bewerte sie im Wiki!</p>', | |||
// ↓↓↓ BILDER HIER EINTRAGEN (finale URLs aus deinem Wiki!) | |||
images: [ | |||
{ | |||
src: 'South Islay Single Malt 13 year-old (Sherry Octave Cask Finish).single.jpg', // ERSETZEN | |||
alt: 'South Islay 13y – Sherry Octave Cask Finish', | |||
link: 'https://ados-wiki.de/wiki/South_Islay_Single_Malt_13_year-old_(Sherry_Octave_Cask_Finish)', | |||
cta: 'Zur South Islay Abfüllung' | |||
}, | |||
{ | |||
src: 'Datei:Tullibardine 13 year-old (Shiraz Wine Octave Cask Finish).single.jpg', // ERSETZEN | |||
alt: 'Tullibardine 13y – Shiraz Wine Octave Cask Finish', | |||
link: 'https://ados-wiki.de/wiki/Tullibardine_13_year-old', | |||
cta: 'Zur Tullibardine Abfüllung' | |||
} | |||
], | |||
// Anzeige | |||
showOnNamespaces: 'all', | |||
dailyLimit: 1, | |||
escToClose: true, | |||
clickBackdropToClose: true | |||
}; | |||
if (!CONFIG.enabled) return; | |||
var ns = mw.config.get('wgNamespaceNumber'); | |||
if (CONFIG.showOnNamespaces !== 'all' && | |||
$.isArray(CONFIG.showOnNamespaces) && | |||
$.inArray(ns, CONFIG.showOnNamespaces) === -1) return; | |||
// 1x/Tag | |||
var isAnon = (mw.config.get('wgUserName') === null); | |||
function LSget(k){ try { return localStorage.getItem(k); } catch(e){ return null; } } | |||
function LSset(k,v){ try { localStorage.setItem(k,v); } catch(e){} } | |||
var key = 'popup_' + CONFIG.id + (isAnon?':anon':':user'); | |||
var today = (function(d){ return d.getFullYear()+'-'+('0'+(d.getMonth()+1)).slice(-2)+'-'+('0'+d.getDate()).slice(-2); })(new Date()); | |||
if (LSget(key) === today) return; | |||
function markSeen(){ LSset(key, today); } | |||
$(function(){ | |||
// Overlay + Modal | |||
var $overlay = $('<div>', {'class':'mw-popup-overlay'}); | |||
var $modal = $('<div>', { | |||
'class':'mw-popup-modal', | |||
'role':'dialog', | |||
'aria-modal':'true', | |||
'aria-labelledby':'mw-news-title' | |||
}); | |||
// Canvas-Bühne (Whisky-Glas) | |||
var $canvasWrap = $('<div>', {'class':'mw-fw-canvas-wrap'}); | |||
var $canvas = $('<canvas>', {'class':'mw-fw-canvas','aria-hidden':'true'}); | |||
$canvasWrap.append($canvas); | |||
// Titel + Einleitung | |||
var $title = $('<h2>', { id:'mw-news-title' }).text(CONFIG.title); | |||
var $intro = $('<div>', {'class':'mw-popup-content'}).html(CONFIG.introHTML); | |||
// Bildkarten | |||
var $cards = $('<div>', {'class':'mw-wnews-cards'}); | |||
CONFIG.images.forEach(function(img){ | |||
var $card = $('<a>', { | |||
'class':'mw-wnews-card', | |||
'href': img.link, | |||
'target':'_blank', | |||
'rel':'noopener' | |||
}); | |||
$card.append( | |||
$('<div>', {'class':'mw-wnews-thumb'}).append( | |||
$('<img>', { src: img.src, alt: img.alt, loading:'lazy' }) | |||
), | |||
$('<div>', {'class':'mw-wnews-meta'}).append( | |||
$('<div>', {'class':'mw-wnews-title', 'text': img.alt}), | |||
$('<div>', {'class':'mw-wnews-cta', 'text': img.cta || 'Mehr ansehen'}) | |||
) | |||
); | |||
$cards.append($card); | |||
}); | |||
// Buttons | |||
var $btnRow = $('<div>', {'class':'mw-popup-button-row'}); | |||
var $ok = $('<button>', {'class':'mw-popup-close', type:'button'}).text('OK'); | |||
$btnRow.append($ok); | |||
// Zusammenbauen | |||
$modal.append($canvasWrap, $title, $intro, $cards, $btnRow); | |||
$('body').append($overlay, $modal); | |||
// Schließen | |||
function close(){ | |||
stopAnim(); | |||
markSeen(); | |||
$overlay.remove(); $modal.remove(); | |||
$(document).off('keydown.mwwnews'); | |||
} | |||
if (CONFIG.clickBackdropToClose) $overlay.on('click', close); | |||
$ok.on('click', close); | |||
if (CONFIG.escToClose) { | |||
$(document).on('keydown.mwwnews', function(e){ | |||
var k = e.key || e.keyCode; | |||
if (k==='Escape' || k==='Esc' || k===27){ e.preventDefault(); close(); } | |||
}); | |||
} | |||
// ==== Whisky-Animation (Lowball-Glas) ==== | |||
var canvas = $canvas[0], ctx = canvas.getContext('2d'); | |||
var dpr = Math.max(1, window.devicePixelRatio || 1); | |||
var w=0,h=0, raf=null, t0=0, reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; | |||
function resize(){ | |||
var r = $canvasWrap[0].getBoundingClientRect(); | |||
w = Math.floor(r.width * dpr); | |||
h = Math.floor(r.height * dpr); | |||
canvas.width = w; canvas.height = h; | |||
canvas.style.width = r.width+'px'; | |||
canvas.style.height = r.height+'px'; | |||
} | |||
function drawGlass(){ | |||
// Glas-Form | |||
var gx = w*0.18, gy = h*0.15, gw = w*0.64, gh = h*0.7, radius = Math.min(gw,gh)*0.08; | |||
ctx.save(); | |||
ctx.globalAlpha = 1; | |||
// Rand | |||
ctx.strokeStyle = 'rgba(255,255,255,0.3)'; | |||
ctx.lineWidth = Math.max(2, 2*dpr); | |||
ctx.beginPath(); | |||
ctx.moveTo(gx+radius, gy); | |||
ctx.arcTo(gx+gw, gy, gx+gw, gy+gh, radius); | |||
ctx.arcTo(gx+gw, gy+gh, gx, gy+gh, radius); | |||
ctx.arcTo(gx, gy+gh, gx, gy, radius); | |||
ctx.arcTo(gx, gy, gx+gw, gy, radius); | |||
ctx.closePath(); | |||
ctx.stroke(); | |||
// Glanz | |||
ctx.beginPath(); | |||
ctx.moveTo(gx+gw*0.15, gy+gh*0.1); | |||
ctx.quadraticCurveTo(gx+gw*0.25, gy+gh*0.05, gx+gw*0.3, gy+gh*0.3); | |||
ctx.strokeStyle = 'rgba(255,255,255,0.18)'; | |||
ctx.stroke(); | |||
ctx.restore(); | |||
return {x:gx,y:gy,w:gw,h:gh}; | |||
} | |||
var bubbles = []; | |||
function resetBubble(b, liquidTop, glass){ | |||
b.x = glass.x + Math.random()*glass.w*0.96 + glass.w*0.02; | |||
b.y = glass.y + glass.h - Math.random()*10*dpr; | |||
b.r = Math.random()*1.8*dpr + 0.8*dpr; | |||
b.speed = (Math.random()*0.2 + 0.1)*dpr; | |||
b.wobble = Math.random()*0.8 + 0.4; | |||
b.hue = 30 + Math.random()*20; // bernsteinfarben | |||
b.lTop = liquidTop; | |||
} | |||
function drawFrame(ts){ | |||
if (!t0) t0 = ts; | |||
var t = (ts - t0)/1000; | |||
ctx.globalCompositeOperation = 'source-over'; | |||
ctx.fillStyle = 'rgba(5,10,20,0.15)'; // leicht abdunkeln für Trail | |||
ctx.fillRect(0,0,w,h); | |||
// Glas | |||
var glass = drawGlass(); | |||
// Flüssigkeit: Wellenhöhe | |||
var base = glass.y + glass.h*0.58; | |||
var amp = Math.min(14*dpr, h*0.03); | |||
var wave = Math.sin(t*2.2)*amp; | |||
var liquidTop = base + wave; | |||
// Whisky-Füllung | |||
var grd = ctx.createLinearGradient(0, liquidTop-30*dpr, 0, glass.y+glass.h); | |||
grd.addColorStop(0, 'rgba(255,190,90,0.95)'); | |||
grd.addColorStop(1, 'rgba(170,85,20,0.98)'); | |||
ctx.save(); | |||
ctx.beginPath(); | |||
// Clip in Glas | |||
var radius = Math.min(glass.w,glass.h)*0.08; | |||
ctx.moveTo(glass.x+radius, glass.y); | |||
ctx.arcTo(glass.x+glass.w, glass.y, glass.x+glass.w, glass.y+glass.h, radius); | |||
ctx.arcTo(glass.x+glass.w, glass.y+glass.h, glass.x, glass.y+glass.h, radius); | |||
ctx.arcTo(glass.x, glass.y+glass.h, glass.x, glass.y, radius); | |||
ctx.arcTo(glass.x, glass.y, glass.x+glass.w, glass.y, radius); | |||
ctx.closePath(); | |||
ctx.clip(); | |||
// Flüssigkeit füllen | |||
ctx.fillStyle = grd; | |||
ctx.fillRect(glass.x, liquidTop, glass.w, glass.y+glass.h - liquidTop); | |||
// Wellenlinie | |||
ctx.beginPath(); | |||
ctx.moveTo(glass.x, liquidTop); | |||
for (var x=0; x<=glass.w; x+=6*dpr){ | |||
var y = liquidTop + Math.sin((x*0.05) + t*3.2)*amp*0.2; | |||
ctx.lineTo(glass.x + x, y); | |||
} | |||
ctx.lineTo(glass.x+glass.w, glass.y+glass.h); | |||
ctx.lineTo(glass.x, glass.y+glass.h); | |||
ctx.closePath(); | |||
ctx.fillStyle = 'rgba(255,165,70,0.3)'; | |||
ctx.fill(); | |||
// Bläschen | |||
if (bubbles.length === 0){ | |||
for (var i=0;i<120;i++){ bubbles.push({}); resetBubble(bubbles[i], liquidTop, glass); } | |||
} | |||
ctx.globalCompositeOperation = 'lighter'; | |||
bubbles.forEach(function(b){ | |||
// nur in der Flüssigkeit zeichnen | |||
if (b.y < liquidTop + 3*dpr) { | |||
// neu starten, wenn Oberfläche erreicht | |||
resetBubble(b, liquidTop, glass); | |||
} else { | |||
// Auftrieb + leichtes Wabern | |||
b.y -= b.speed * (1 + Math.sin(t*3 + b.x*0.02)*0.2); | |||
b.x += Math.sin(t*2 + b.y*0.02)*b.wobble*0.15; | |||
} | |||
ctx.beginPath(); | |||
ctx.fillStyle = 'rgba(255,220,140,0.9)'; | |||
ctx.arc(b.x, b.y, b.r, 0, Math.PI*2); | |||
ctx.fill(); | |||
// Glanzpunkt | |||
ctx.beginPath(); | |||
ctx.fillStyle = 'rgba(255,255,255,0.6)'; | |||
ctx.arc(b.x - b.r*0.3, b.y - b.r*0.3, b.r*0.35, 0, Math.PI*2); | |||
ctx.fill(); | |||
}); | |||
ctx.restore(); // Clip beenden | |||
// Kleine Highlights am Glasrand | |||
ctx.save(); | |||
ctx.globalCompositeOperation = 'lighter'; | |||
ctx.strokeStyle = 'rgba(255,255,255,0.15)'; | |||
ctx.lineWidth = 1*dpr; | |||
ctx.beginPath(); | |||
ctx.arc(glass.x + glass.w*0.85, glass.y + glass.h*0.15, 10*dpr, 0, Math.PI*2); | |||
ctx.stroke(); | |||
ctx.restore(); | |||
raf = requestAnimationFrame(drawFrame); | |||
} | |||
function startAnim(){ | |||
if (reduce) return; | |||
resize(); | |||
t0 = 0; | |||
raf = requestAnimationFrame(drawFrame); | |||
window.addEventListener('resize', resize); | |||
} | |||
function stopAnim(){ | |||
if (raf){ cancelAnimationFrame(raf); raf=null; } | |||
window.removeEventListener('resize', resize); | |||
} | |||
startAnim(); | |||
markSeen(); | |||
}); | |||
})(jQuery, mw); | |||
}); | }); | ||