| HTML |
|---|
// Based on Flame by: // Created by anatole duprat - XT95/2013 // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. <style> /* Target Confluence's main content area */ #main-content, .wiki-content, .contentLayout2, .cell, #content { position: relative !important; } /* Make the background div cover the entire content area */ #flameBackground { position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100% !important; height: 100% !important; z-index: 0 !important; pointer-events: none !important; } #flameCanvas { width: 100%; height: 100%; display: block; } /* Ensure actual page content is rendered on top */ .wiki-content > *:not(#flameBackground), .contentLayout2 > *:not(#flameBackground) { position: relative; z-index: 1; } /* Text readability */ .wiki-content h1, .wiki-content h2, .wiki-content h3, .wiki-content p { text-shadow: 0 2px 8px rgba(0,0,0,0.7); } </style> <div id="flameBackground"> <canvas id="flameCanvas"></canvas> </div> <script> // Based on Flame by: // Created by anatole duprat - XT95/2013 // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. (function() { const canvas = document.getElementById('flameCanvas'); if (!canvas) return; // Максимальное качество контекста const gl = canvas.getContext('webgl', { antialias: true, // Включено сглаживание alpha: true, depth: false, stencil: false, powerPreference: 'high-performance' // Требуем макс. производительность от GPU }) || canvas.getContext('experimental-webgl'); if (!gl) return; const maxSteps = 64; // Максимум шагов для детализации const resolutionScale = 1.0; // 100% разрешение const vertexShaderSource = ` attribute vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); } `; const fragmentShaderSource = ` precision highp float; // Высокая точность вычислений uniform vec2 iResolution; uniform float iTime; uniform int maxSteps; float noise(vec3 p) { vec3 i = floor(p); vec4 a = dot(i, vec3(1., 57., 21.)) + vec4(0., 57., 21., 78.); vec3 f = cos((p-i)*acos(-1.))*(-.5)+.5; a = mix(sin(cos(a)*a),sin(cos(1.+a)*(1.+a)), f.x); a.xy = mix(a.xz, a.yw, f.y); return mix(a.x, a.y, f.z); } float sphere(vec3 p, vec4 spr) { return length(spr.xyz-p) - spr.w; } float flame(vec3 p) { float d = sphere(p*vec3(1.,.5,1.), vec4(.0,-1.,.0,1.)); return d + (noise(p+vec3(.0,iTime*2.,.0)) + noise(p*3.)*.5)*.25*(p.y); } float scene(vec3 p) { return min(100.-length(p), abs(flame(p))); } vec4 raymarch(vec3 org, vec3 dir) { float d = 0.0, glow = 0.0, eps = 0.02; vec3 p = org; bool glowed = false; // Цикл отрисовки for(int i=0; i<64; i++) { if(i >= maxSteps) break; d = scene(p) + eps; p += d * dir; if(d > eps) { if(flame(p) < .0) glowed = true; if(glowed) glow = float(i)/float(maxSteps); } } return vec4(p, glow); } void main() { vec2 fragCoord = gl_FragCoord.xy; vec2 v = -1.0 + 2.0 * fragCoord.xy / iResolution.xy; v.x *= iResolution.x/iResolution.y; vec3 org = vec3(0., -2., 14.); vec3 dir = normalize(vec3(v.x*1.6, -v.y, -1.5)); vec4 p = raymarch(org, dir); float glow = p.w; vec4 col = mix(vec4(1.,.5,.1,1.), vec4(0.1,.5,1.,1.), p.y*.02+.4); gl_FragColor = mix(vec4(0.), col, pow(glow*2., 4.)); } `; function compileShader(source, type) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error('Shader compile error:', gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } const vs = compileShader(vertexShaderSource, gl.VERTEX_SHADER); const fs = compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER); if (!vs || !fs) return; const program = gl.createProgram(); gl.attachShader(program, vs); gl.attachShader(program, fs); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error('Program link error:', gl.getProgramInfoLog(program)); return; } gl.useProgram(program); const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,1,1]), gl.STATIC_DRAW); const pos = gl.getAttribLocation(program, 'position'); gl.enableVertexAttribArray(pos); gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0); const iRes = gl.getUniformLocation(program, 'iResolution'); const iTime = gl.getUniformLocation(program, 'iTime'); const maxStepsUniform = gl.getUniformLocation(program, 'maxSteps'); gl.uniform1i(maxStepsUniform, maxSteps); function resize() { const container = canvas.parentElement; if (!container) return; const rect = container.getBoundingClientRect(); const displayWidth = rect.width || window.innerWidth; const displayHeight = rect.height || window.innerHeight; // Установка полного разрешения без масштабирования canvas.width = Math.floor(displayWidth * resolutionScale); canvas.height = Math.floor(displayHeight * resolutionScale); gl.viewport(0, 0, canvas.width, canvas.height); } // Вставка фона в контент setTimeout(function() { const bg = document.getElementById('flameBackground'); const content = document.querySelector('.wiki-content') || document.querySelector('#main-content') || document.querySelector('.contentLayout2') || document.querySelector('#content'); if (content && bg) { content.insertBefore(bg, content.firstChild); resize(); } }, 100); window.addEventListener('resize', resize); const start = Date.now(); let animationId = null; let isPageVisible = true; // Остановка только если вкладка полностью скрыта (стандарт браузера) document.addEventListener('visibilitychange', function() { isPageVisible = !document.hidden; if (isPageVisible && !animationId) { animationId = requestAnimationFrame(render); } }); function render() { if (!isPageVisible || gl.isContextLost()) { animationId = null; return; } // Рендер без ограничения FPS - максимальная плавность gl.uniform2f(iRes, canvas.width, canvas.height); gl.uniform1f(iTime, (Date.now() - start) / 1000); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); animationId = requestAnimationFrame(render); } animationId = requestAnimationFrame(render); // Очистка при удалении элемента if (window.MutationObserver) { const observer = new MutationObserver(function(mutations) { if (!document.body.contains(canvas)) { if (animationId) { cancelAnimationFrame(animationId); animationId = null; } observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); } })(); </script> |
...