Mediawiki:common.js
| Строка 2222: | Строка 2222: | ||
var AprilFoolsCubeTransitionModule = { | var AprilFoolsCubeTransitionModule = { | ||
config: { | |||
// Пока только по флагу: | |||
enableForAll: false, // <-- чтобы включить всем, просто поставь true | |||
enableOnAprilFirst: false, // <-- если потом захочешь автозапуск 1 апреля | |||
onlyMainPage: true, // запуск только на главной | |||
runOncePerSession: true, // работает только для обычного режима (НЕ для ?prank=1) | |||
overlayDurationMs: 4200, // сколько показывать картинку до куб-перехода | |||
cubeDurationMs: 1200, | |||
wikiImageUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/d4/2019_Screenshot_of_English_Wikipedia_homepage.png', | |||
clockAudioUrl: 'https://zvukogram.com/mp3/34/the-ticking-of-a-quartz-clock.mp3', | |||
lampAudioUrl: 'https://zvukitop.com/wp-content/uploads/2021/04/luminesc-lampa-r4fh.mp3' | |||
}, | |||
started: false, | |||
overlay: null, | overlay: null, | ||
clockAudio: null, | clockAudio: null, | ||
lampAudio: null, | lampAudio: null, | ||
needsGestureUnlock: false, | |||
init: function () { | init: function () { | ||
| Строка 2238: | Строка 2247: | ||
this.started = true; | this.started = true; | ||
if (!window.mw || !mw.config || !mw.config.get('wgIsMainPage')) { | if (this.config.onlyMainPage) { | ||
if (!window.mw || !mw.config || !mw.config.get('wgIsMainPage')) { | |||
return; | |||
} | |||
} | } | ||
var url = new URL(window.location.href); | var url = new URL(window.location.href); | ||
var | var prankFlag = url.searchParams.get('prank') === '1'; | ||
var now = new Date(); | var now = new Date(); | ||
var isAprilFirst = now.getMonth() === 3 && now.getDate() === 1; | var isAprilFirst = now.getMonth() === 3 && now.getDate() === 1; | ||
var shouldRun = | var shouldRun = | ||
if (!shouldRun) { | prankFlag || | ||
return; | this.config.enableForAll || | ||
(this.config.enableOnAprilFirst && isAprilFirst); | |||
if (!shouldRun) return; | |||
// Важно: для ?prank=1 НЕ ограничиваем одним показом | |||
if (!prankFlag && this.config.runOncePerSession) { | |||
if (sessionStorage.getItem('april-fools-cube-shown') === '1') { | |||
return; | |||
} | |||
sessionStorage.setItem('april-fools-cube-shown', '1'); | |||
} | } | ||
this.injectStyles(); | this.injectStyles(); | ||
this. | this.createOverlay(); | ||
this. | this.prepareAudio(); | ||
this. | this.startSequence(); | ||
}, | }, | ||
injectStyles: function () { | injectStyles: function () { | ||
if (document.getElementById('april-cube- | if (document.getElementById('april-fools-cube-v2-styles')) return; | ||
var style = document.createElement('style'); | var style = document.createElement('style'); | ||
style.id = 'april-cube- | style.id = 'april-fools-cube-v2-styles'; | ||
style.textContent = | style.textContent = | ||
'#april-cube | '#april-fools-cube-overlay {' + | ||
'position:fixed;' + | 'position: fixed;' + | ||
'inset:0;' + | 'inset: 0;' + | ||
'z-index:2147483647;' + | 'z-index: 2147483647;' + | ||
' | 'pointer-events: auto;' + | ||
' | 'background: #000;' + | ||
'overflow: hidden;' + | |||
'overflow:hidden | |||
'}' + | '}' + | ||
'#april-cube | '#april-fools-cube-overlay .af-scene {' + | ||
' | 'position: absolute;' + | ||
' | 'inset: 0;' + | ||
'perspective: 1800px;' + | |||
'perspective-origin: 50% 50%;' + | |||
'overflow: hidden;' + | |||
'}' + | '}' + | ||
'#april-cube- | '#april-fools-cube-overlay .af-cube {' + | ||
'position:absolute;' + | 'position: absolute;' + | ||
'inset:0;' + | 'inset: 0;' + | ||
' | 'transform-style: preserve-3d;' + | ||
' | 'transform-origin: right center;' + | ||
'- | 'will-change: transform, opacity;' + | ||
'}' + | '}' + | ||
'#april-cube- | '#april-fools-cube-overlay.af-exit .af-cube {' + | ||
' | 'animation: af-cube-rotate 1200ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;' + | ||
'}' + | '}' + | ||
'#april-cube- | '#april-fools-cube-overlay .af-face {' + | ||
' | 'position: absolute;' + | ||
'inset: 0;' + | |||
'backface-visibility: hidden;' + | |||
'-webkit-backface-visibility: hidden;' + | |||
'overflow: hidden;' + | |||
'}' + | '}' + | ||
' | '#april-fools-cube-overlay .af-front {' + | ||
'transform: translateZ(1px);' + | |||
'background: #000;' + | |||
'}' + | '}' + | ||
' | '#april-fools-cube-overlay .af-front img {' + | ||
' | 'display: block;' + | ||
' | 'width: 100%;' + | ||
' | 'height: 100%;' + | ||
' | 'object-fit: cover;' + | ||
' | 'object-position: center top;' + | ||
'user-select: none;' + | |||
'-webkit-user-drag: none;' + | |||
'pointer-events: none;' + | |||
'}' + | '}' + | ||
' | '#april-fools-cube-overlay .af-right {' + | ||
'transform: | 'transform-origin: left center;' + | ||
'transform: rotateY(90deg) translateX(0);' + | |||
// прозрачная грань: видна реальная страница как "другая сторона куба" | |||
'background: transparent;' + | |||
'box-shadow: inset 0 0 120px rgba(0, 0, 0, 0.35);' + | |||
'}' + | '}' + | ||
' | '#april-fools-cube-overlay .af-edge {' + | ||
' | 'position: absolute;' + | ||
'top: 0;' + | |||
'right: 0;' + | |||
'width: 36px;' + | |||
'height: 100%;' + | |||
'background: linear-gradient(to left, rgba(0,0,0,0.45), rgba(0,0,0,0));' + | |||
'pointer-events: none;' + | |||
'}' + | '}' + | ||
' | '@keyframes af-cube-rotate {' + | ||
' | '0% {' + | ||
'transform: translateZ(0) rotateY(0deg);' + | |||
'opacity: 1;' + | |||
'}' + | |||
'100% {' + | |||
'transform: translateZ(0) rotateY(-90deg);' + | |||
'opacity: 0.02;' + | |||
'}' + | |||
' | |||
' | |||
'}'; | '}'; | ||
document.head.appendChild(style); | document.head.appendChild(style); | ||
}, | }, | ||
createOverlay: function () { | |||
var overlay = document.createElement('div'); | var overlay = document.createElement('div'); | ||
overlay.id = 'april-cube | overlay.id = 'april-fools-cube-overlay'; | ||
var scene = document.createElement('div'); | var scene = document.createElement('div'); | ||
scene. | scene.className = 'af-scene'; | ||
var cube = document.createElement('div'); | var cube = document.createElement('div'); | ||
cube. | cube.className = 'af-cube'; | ||
var front = document.createElement('div'); | var front = document.createElement('div'); | ||
front.className = ' | front.className = 'af-face af-front'; | ||
var img = document.createElement('img'); | var img = document.createElement('img'); | ||
img.src = | img.src = this.config.wikiImageUrl; | ||
img.alt = 'Wikipedia | img.alt = 'Wikipedia homepage screenshot'; | ||
var right = document.createElement('div'); | var right = document.createElement('div'); | ||
right.className = ' | right.className = 'af-face af-right'; | ||
var | var edge = document.createElement('div'); | ||
edge.className = 'af-edge'; | |||
front.appendChild(img); | |||
cube.appendChild(front); | cube.appendChild(front); | ||
cube.appendChild(right); | cube.appendChild(right); | ||
cube.appendChild(edge); | |||
scene.appendChild(cube); | scene.appendChild(cube); | ||
overlay.appendChild(scene); | overlay.appendChild(scene); | ||
document.body.appendChild(overlay); | document.body.appendChild(overlay); | ||
this.overlay = overlay; | this.overlay = overlay; | ||
}, | }, | ||
prepareAudio: function () { | |||
this.clockAudio = new Audio(this.config.clockAudioUrl); | |||
this.lampAudio = new Audio(this.config.lampAudioUrl); | |||
this.clockAudio.preload = 'auto'; | |||
this.lampAudio.preload = 'auto'; | |||
this.clockAudio.loop = true; | this.clockAudio.loop = true; | ||
this.clockAudio.volume = 0.45; | this.clockAudio.volume = 0.45; | ||
this.lampAudio.loop = false; | this.lampAudio.loop = false; | ||
this.lampAudio.volume = 0.6; | |||
this.lampAudio.volume = 0. | |||
}, | }, | ||
startSequence: function () { | |||
var self = this; | |||
this. | |||
// Пробуем запустить сразу оба звука одновременно | |||
this.playBothSoundsNow(); | |||
setTimeout(function () { | |||
self.startCubeTransition(); | |||
}, this.config.overlayDurationMs); | |||
}, | }, | ||
playBothSoundsNow: function () { | |||
var self = this; | |||
this. | var playClock = this.clockAudio ? this.clockAudio.play() : Promise.resolve(); | ||
var playLamp = this.lampAudio ? this.lampAudio.play() : Promise.resolve(); | |||
Promise.allSettled([playClock, playLamp]).then(function (results) { | |||
var hasRejected = results.some(function (r) { return r.status === 'rejected'; }); | |||
if (hasRejected) { | |||
} | self.enableGestureUnlock(); | ||
} | |||
}); | |||
}, | }, | ||
enableGestureUnlock: function () { | |||
if (this.needsGestureUnlock) return; | |||
this.needsGestureUnlock = true; | |||
var self = this; | var self = this; | ||
function unlockAndPlay() { | |||
self.needsGestureUnlock = false; | |||
if (self.clockAudio) { | if (self.clockAudio) { | ||
self.clockAudio.currentTime = 0; | |||
self.clockAudio.play().catch(function () {}); | self.clockAudio.play().catch(function () {}); | ||
} | } | ||
if (self.lampAudio) { | |||
self.lampAudio.currentTime = 0; | |||
self.lampAudio.play().catch(function () {}); | |||
} | |||
document.removeEventListener('pointerdown', unlockAndPlay); | document.removeEventListener('pointerdown', unlockAndPlay); | ||
document.removeEventListener('keydown', unlockAndPlay); | document.removeEventListener('keydown', unlockAndPlay); | ||
| Строка 2444: | Строка 2460: | ||
startCubeTransition: function () { | startCubeTransition: function () { | ||
if (!this. | if (!this.overlay) return; | ||
// В момент вращения ещё раз запускаем "лампу", часы продолжают тикать | |||
if (this.lampAudio) { | |||
try { | |||
this.lampAudio.currentTime = 0; | |||
this.lampAudio.play().catch(function () {}); | |||
} catch (e) {} | |||
} | |||
this.overlay.classList.add('af-exit'); | |||
var self = this; | |||
setTimeout(function () { | |||
self.finish(); | |||
}, this.config.cubeDurationMs + 80); | |||
}, | |||
finish: function () { | |||
if (this.clockAudio) { | if (this.clockAudio) { | ||
this.clockAudio.pause(); | this.clockAudio.pause(); | ||
| Строка 2451: | Строка 2484: | ||
} | } | ||
if (this. | if (this.overlay && this.overlay.parentNode) { | ||
this. | this.overlay.parentNode.removeChild(this.overlay); | ||
} | } | ||
this.overlay = null; | |||
this | |||
} | } | ||
}; | }; | ||