/** * @file includes/header-render-funciones.js * @brief Funciones JS para el header: menu radial, dropdown, scroll, touch. * Cargado desde header-render.php dentro de un IIFE. */ (function(){ 'use strict'; var radialItems = [{"title":"Avisos","slug":"avisos"},{"title":"Tienda","slug":"tienda"},{"title":"Utilidades","slug":"utilidades"},{"title":"Domotica","slug":"domotica"},{"title":"Contacto","slug":"contacto"},{"title":"Empresa","slug":"empresa"},{"title":"Aprende","slug":"conocimiento"}]; var needsOuterRing = radialItems.length > 8; var SEGMENTS = 8; var CX = 200, CY = 200; var R1_COLORS = ['#1A5E3A', '#5A2A18']; var R1_HOVER = ['#00C853', '#FF4000']; var R2_COLORS = ['#164E30', '#4A2210']; var R2_HOVER = ['#00C853', '#FF4000']; var EMPTY_R1 = '#12121e'; var EMPTY_R2 = '#0e0e1a'; var CENTER_FILL = '#00C853'; var CENTER_STROKE = '#009E42'; var CENTER_HOVER_STROKE = '#00E676'; var GAP = 1.8; var isOpen = false; var RING1_IR = 55, RING1_OR = 125; var RING2_IR = 133, RING2_OR = 195; var SLICE_HOVER = 1.14; var CENTER_HOVER = 1.18; var activeTouch = null; function toRad(deg) { return (deg - 90) * Math.PI / 180; } function donutPath(cx, cy, ir, or_, a1, a2) { ir = Math.max(0.01, ir); or_ = Math.max(0.02, or_); var x1 = cx + ir * Math.cos(toRad(a1)), y1 = cy + ir * Math.sin(toRad(a1)); var x2 = cx + or_ * Math.cos(toRad(a1)), y2 = cy + or_ * Math.sin(toRad(a1)); var x3 = cx + or_ * Math.cos(toRad(a2)), y3 = cy + or_ * Math.sin(toRad(a2)); var x4 = cx + ir * Math.cos(toRad(a2)), y4 = cy + ir * Math.sin(toRad(a2)); var lg = (a2 - a1) > 180 ? 1 : 0; return 'M '+x1+' '+y1+' L '+x2+' '+y2+' A '+or_+' '+or_+' 0 '+lg+' 1 '+x3+' '+y3+' L '+x4+' '+y4+' A '+ir+' '+ir+' 0 '+lg+' 0 '+x1+' '+y1+' Z'; } function clearAnimAfterPlay(el, delayMs) { setTimeout(function() { el.style.animation = 'none'; el.style.opacity = '1'; el.style.transform = 'scale(1)'; }, delayMs + 400); } function hoverOn(grp, path, text, hoverColor, activeColor) { grp.style.transform = 'scale(' + SLICE_HOVER + ')'; grp.style.filter = 'drop-shadow(0 0 14px ' + hoverColor + '77)'; path.setAttribute('fill', hoverColor); text.setAttribute('fill', activeColor); } function hoverOff(grp, path, text, baseColor, defaultColor) { grp.style.transform = 'scale(1)'; grp.style.filter = ''; path.setAttribute('fill', baseColor); text.setAttribute('fill', defaultColor); } function revertTouch() { if (activeTouch) { hoverOff(activeTouch.grp, activeTouch.path, activeTouch.text, activeTouch.baseColor, activeTouch.defaultColor); activeTouch = null; } } function findSliceData(el) { var node = el; while (node && node !== document) { if (node._sliceData) return node._sliceData; node = node.parentNode; } return null; } function buildMenu() { var svg = document.getElementById('radial-svg'); var ns = 'http://www.w3.org/2000/svg'; svg.innerHTML = ''; var segAngle = 360 / SEGMENTS; var allArcs = []; activeTouch = null; for (var i = 0; i < SEGMENTS; i++) { var a1 = i * segAngle + GAP / 2; var a2 = (i + 1) * segAngle - GAP / 2; var midA = (a1 + a2) / 2; var item = radialItems[i] || null; var ci = i % 2; var delay = i * 40; var g = document.createElementNS(ns, 'g'); g.style.transformOrigin = CX + 'px ' + CY + 'px'; g.style.transition = 'transform .28s ease-out, filter .28s ease-out'; g.style.opacity = '0'; g.style.animation = 'radialSliceIn .35s ease-out ' + (delay) + 'ms forwards'; clearAnimAfterPlay(g, delay); var path = document.createElementNS(ns, 'path'); path.setAttribute('d', donutPath(CX, CY, RING1_IR, RING1_OR, a1, a2)); path.setAttribute('fill', item ? R1_COLORS[ci] : EMPTY_R1); path.setAttribute('stroke', '#0a0a1a'); path.setAttribute('stroke-width', '2'); path.style.cursor = item ? 'pointer' : 'default'; g.appendChild(path); if (item) { var textR = RING1_OR - 14; var isBot = (midA > 90 && midA < 270); var ta1 = isBot ? a2 : a1, ta2 = isBot ? a1 : a2; var sw = isBot ? 0 : 1; var tx1 = CX + textR * Math.cos(toRad(ta1)), ty1 = CY + textR * Math.sin(toRad(ta1)); var tx2 = CX + textR * Math.cos(toRad(ta2)), ty2 = CY + textR * Math.sin(toRad(ta2)); var arcId = 'r1a' + i; var arcD = 'M '+tx1+','+ty1+' A '+textR+','+textR+' 0 0,'+sw+' '+tx2+','+ty2; allArcs.push({ arcId: arcId, arcD: arcD }); var text = document.createElementNS(ns, 'text'); text.setAttribute('fill', '#C0C8C4'); text.setAttribute('font-size', '12'); text.setAttribute('font-weight', '700'); text.setAttribute('font-family', 'system-ui, -apple-system, sans-serif'); text.setAttribute('letter-spacing', '0.5'); text.setAttribute('pointer-events', 'none'); text.style.opacity = '0'; text.style.transition = 'opacity .3s ease-out'; var tp = document.createElementNS(ns, 'textPath'); tp.setAttribute('href', '#' + arcId); tp.setAttribute('startOffset', '50%'); tp.setAttribute('text-anchor', 'middle'); tp.textContent = item.title.toUpperCase(); text.appendChild(tp); g.appendChild(text); (function(el) { setTimeout(function() { el.style.opacity = '1'; }, delay + 200); })(text); var sliceData = { grp: g, path: path, text: text, baseColor: R1_COLORS[ci], hoverColor: R1_HOVER[ci], defaultColor: '#C0C8C4', activeColor: '#FFFFFF', slug: item.slug }; g._sliceData = sliceData; path._sliceData = sliceData; (function(sd) { sd.grp.addEventListener('mouseenter', function() { hoverOn(sd.grp, sd.path, sd.text, sd.hoverColor, sd.activeColor); }); sd.grp.addEventListener('mouseleave', function() { hoverOff(sd.grp, sd.path, sd.text, sd.baseColor, sd.defaultColor); }); sd.grp.addEventListener('click', function(e) { e.preventDefault(); closeRadialMenu(); window.location.href = '/' + sd.slug; }); })(sliceData); } svg.appendChild(g); } if (needsOuterRing) { for (var j = 0; j < SEGMENTS; j++) { var idx = 8 + j; var a1 = j * segAngle + GAP / 2; var a2 = (j + 1) * segAngle - GAP / 2; var midA = (a1 + a2) / 2; var item = radialItems[idx] || null; var ci = j % 2; var delay = 350 + j * 40; var g = document.createElementNS(ns, 'g'); g.style.transformOrigin = CX + 'px ' + CY + 'px'; g.style.transition = 'transform .28s ease-out, filter .28s ease-out'; g.style.opacity = '0'; g.style.animation = 'radialSliceIn .35s ease-out ' + (delay) + 'ms forwards'; clearAnimAfterPlay(g, delay); var path = document.createElementNS(ns, 'path'); path.setAttribute('d', donutPath(CX, CY, RING2_IR, RING2_OR, a1, a2)); path.setAttribute('fill', item ? R2_COLORS[ci] : EMPTY_R2); path.setAttribute('stroke', '#0a0a1a'); path.setAttribute('stroke-width', '1.5'); path.style.cursor = item ? 'pointer' : 'default'; g.appendChild(path); if (item) { var textR = RING2_OR - 14; var isBot = (midA > 90 && midA < 270); var ta1 = isBot ? a2 : a1, ta2 = isBot ? a1 : a2; var sw = isBot ? 0 : 1; var tx1 = CX + textR * Math.cos(toRad(ta1)), ty1 = CY + textR * Math.sin(toRad(ta1)); var tx2 = CX + textR * Math.cos(toRad(ta2)), ty2 = CY + textR * Math.sin(toRad(ta2)); var arcId = 'r2a' + j; var arcD = 'M '+tx1+','+ty1+' A '+textR+','+textR+' 0 0,'+sw+' '+tx2+','+ty2; allArcs.push({ arcId: arcId, arcD: arcD }); var text = document.createElementNS(ns, 'text'); text.setAttribute('fill', '#A0A8A4'); text.setAttribute('font-size', '11'); text.setAttribute('font-weight', '700'); text.setAttribute('font-family', 'system-ui, -apple-system, sans-serif'); text.setAttribute('letter-spacing', '0.4'); text.setAttribute('pointer-events', 'none'); text.style.opacity = '0'; text.style.transition = 'opacity .3s ease-out'; var tp = document.createElementNS(ns, 'textPath'); tp.setAttribute('href', '#' + arcId); tp.setAttribute('startOffset', '50%'); tp.setAttribute('text-anchor', 'middle'); tp.textContent = item.title.toUpperCase(); text.appendChild(tp); g.appendChild(text); (function(el) { setTimeout(function() { el.style.opacity = '1'; }, delay + 200); })(text); var sliceData = { grp: g, path: path, text: text, baseColor: R2_COLORS[ci], hoverColor: R2_HOVER[ci], defaultColor: '#A0A8A4', activeColor: '#FFFFFF', slug: item.slug }; g._sliceData = sliceData; path._sliceData = sliceData; (function(sd) { sd.grp.addEventListener('mouseenter', function() { hoverOn(sd.grp, sd.path, sd.text, sd.hoverColor, sd.activeColor); }); sd.grp.addEventListener('mouseleave', function() { hoverOff(sd.grp, sd.path, sd.text, sd.baseColor, sd.defaultColor); }); sd.grp.addEventListener('click', function(e) { e.preventDefault(); closeRadialMenu(); window.location.href = '/' + sd.slug; }); })(sliceData); } svg.appendChild(g); } } var defs = document.createElementNS(ns, 'defs'); for (var a = 0; a < allArcs.length; a++) { var arc = document.createElementNS(ns, 'path'); arc.setAttribute('id', allArcs[a].arcId); arc.setAttribute('d', allArcs[a].arcD); arc.setAttribute('fill', 'none'); arc.setAttribute('stroke', 'none'); defs.appendChild(arc); } svg.insertBefore(defs, svg.firstChild); // ── CENTER ── var cg = document.createElementNS(ns, 'g'); cg.style.transformOrigin = CX + 'px ' + CY + 'px'; cg.style.transition = 'transform .28s ease-out, filter .28s ease-out'; cg.style.cursor = 'pointer'; cg.style.opacity = '0'; cg.style.animation = 'radialSliceIn .3s ease-out 350ms forwards'; clearAnimAfterPlay(cg, 350); var circle = document.createElementNS(ns, 'circle'); circle.setAttribute('cx', CX); circle.setAttribute('cy', CY); circle.setAttribute('r', RING1_IR - 8); circle.setAttribute('fill', CENTER_FILL); circle.setAttribute('stroke', CENTER_STROKE); circle.setAttribute('stroke-width', '3'); cg.appendChild(circle); var homeIconEl = document.createElementNS(ns, 'text'); homeIconEl.setAttribute('x', CX); homeIconEl.setAttribute('y', CY - 6); homeIconEl.setAttribute('text-anchor', 'middle'); homeIconEl.setAttribute('dominant-baseline', 'central'); homeIconEl.setAttribute('fill', '#FFFFFF'); homeIconEl.setAttribute('font-size', '24'); homeIconEl.setAttribute('pointer-events', 'none'); homeIconEl.textContent = '\u2302'; cg.appendChild(homeIconEl); var inicioTxtEl = document.createElementNS(ns, 'text'); inicioTxtEl.setAttribute('x', CX); inicioTxtEl.setAttribute('y', CY + 16); inicioTxtEl.setAttribute('text-anchor', 'middle'); inicioTxtEl.setAttribute('dominant-baseline', 'central'); inicioTxtEl.setAttribute('fill', '#FFFFFF'); inicioTxtEl.setAttribute('font-size', '11'); inicioTxtEl.setAttribute('font-weight', '800'); inicioTxtEl.setAttribute('font-family', 'system-ui, -apple-system, sans-serif'); inicioTxtEl.setAttribute('letter-spacing', '1.5'); inicioTxtEl.setAttribute('pointer-events', 'none'); inicioTxtEl.textContent = 'INICIO'; cg.appendChild(inicioTxtEl); svg.appendChild(cg); cg._isCenter = true; circle._isCenter = true; cg.addEventListener('mouseenter', function() { cg.style.transform = 'scale(' + CENTER_HOVER + ')'; cg.style.filter = 'drop-shadow(0 0 20px rgba(0,200,83,0.6))'; circle.setAttribute('stroke', CENTER_HOVER_STROKE); }); cg.addEventListener('mouseleave', function() { cg.style.transform = 'scale(1)'; cg.style.filter = ''; circle.setAttribute('stroke', CENTER_STROKE); }); cg.addEventListener('click', function(e) { e.preventDefault(); closeRadialMenu(); window.location.href = '/'; }); // ── REORDER: Ring2 → Ring1 → Center (painting order) ── if (needsOuterRing) { var kids = []; while (svg.firstChild) { kids.push(svg.firstChild); svg.removeChild(svg.firstChild); } svg.appendChild(kids[0]); // defs var r2Start = 1 + SEGMENTS; var r2End = 1 + SEGMENTS * 2; var centerNode = kids[kids.length - 1]; for (var ri = r2Start; ri < r2End; ri++) svg.appendChild(kids[ri]); // Ring 2 for (var ri = 1; ri < 1 + SEGMENTS; ri++) svg.appendChild(kids[ri]); // Ring 1 svg.appendChild(centerNode); // Center } // ── TOUCH: SVG-level handlers ── var touchNavigating = false; var lastTouchSlice = null; var touchOnCenter = false; function isCenterEl(el) { var node = el; while (node && node !== document) { if (node._isCenter) return true; node = node.parentNode; } return false; } svg.addEventListener('touchstart', function(e) { if (e.touches.length !== 1) return; var t = e.touches[0]; touchNavigating = true; lastTouchSlice = null; touchOnCenter = false; var el = document.elementFromPoint(t.clientX, t.clientY); var sd = findSliceData(el); if (sd) { revertTouch(); hoverOn(sd.grp, sd.path, sd.text, sd.hoverColor, sd.activeColor); activeTouch = sd; lastTouchSlice = sd; } else if (isCenterEl(el)) { revertTouch(); touchOnCenter = true; cg.style.transform = 'scale(' + CENTER_HOVER + ')'; cg.style.filter = 'drop-shadow(0 0 20px rgba(0,200,83,0.6))'; circle.setAttribute('stroke', CENTER_HOVER_STROKE); } }, { passive: true }); svg.addEventListener('touchmove', function(e) { if (!touchNavigating) return; e.preventDefault(); var t = e.touches[0]; var el = document.elementFromPoint(t.clientX, t.clientY); var sd = findSliceData(el); if (sd) { if (lastTouchSlice !== sd) { if (lastTouchSlice) { hoverOff(lastTouchSlice.grp, lastTouchSlice.path, lastTouchSlice.text, lastTouchSlice.baseColor, lastTouchSlice.defaultColor); } if (touchOnCenter) { cg.style.transform = 'scale(1)'; cg.style.filter = ''; circle.setAttribute('stroke', CENTER_STROKE); touchOnCenter = false; } hoverOn(sd.grp, sd.path, sd.text, sd.hoverColor, sd.activeColor); activeTouch = sd; lastTouchSlice = sd; } } else if (isCenterEl(el)) { if (!touchOnCenter) { if (lastTouchSlice) { hoverOff(lastTouchSlice.grp, lastTouchSlice.path, lastTouchSlice.text, lastTouchSlice.baseColor, lastTouchSlice.defaultColor); lastTouchSlice = null; } touchOnCenter = true; cg.style.transform = 'scale(' + CENTER_HOVER + ')'; cg.style.filter = 'drop-shadow(0 0 20px rgba(0,200,83,0.6))'; circle.setAttribute('stroke', CENTER_HOVER_STROKE); } } else { if (lastTouchSlice) { hoverOff(lastTouchSlice.grp, lastTouchSlice.path, lastTouchSlice.text, lastTouchSlice.baseColor, lastTouchSlice.defaultColor); lastTouchSlice = null; } if (touchOnCenter) { cg.style.transform = 'scale(1)'; cg.style.filter = ''; circle.setAttribute('stroke', CENTER_STROKE); touchOnCenter = false; } activeTouch = null; } }, { passive: false }); svg.addEventListener('touchend', function(e) { if (!touchNavigating) return; touchNavigating = false; if (lastTouchSlice) { var slug = lastTouchSlice.slug; hoverOff(lastTouchSlice.grp, lastTouchSlice.path, lastTouchSlice.text, lastTouchSlice.baseColor, lastTouchSlice.defaultColor); lastTouchSlice = null; activeTouch = null; closeRadialMenu(); setTimeout(function() { window.location.href = '/' + slug; }, 50); } else if (touchOnCenter) { touchOnCenter = false; cg.style.transform = 'scale(1)'; cg.style.filter = ''; circle.setAttribute('stroke', CENTER_STROKE); closeRadialMenu(); setTimeout(function() { window.location.href = '/'; }, 50); } else { revertTouch(); } }, { passive: true }); svg.addEventListener('touchcancel', function() { touchNavigating = false; if (lastTouchSlice) { hoverOff(lastTouchSlice.grp, lastTouchSlice.path, lastTouchSlice.text, lastTouchSlice.baseColor, lastTouchSlice.defaultColor); lastTouchSlice = null; } if (touchOnCenter) { cg.style.transform = 'scale(1)'; cg.style.filter = ''; circle.setAttribute('stroke', CENTER_STROKE); touchOnCenter = false; } activeTouch = null; }, { passive: true }); } var style = document.createElement('style'); style.textContent = '@keyframes radialSliceIn{from{opacity:0;transform:scale(0.3) rotate(-20deg)}to{opacity:1;transform:scale(1) rotate(0deg)}}'; document.head.appendChild(style); var overlay = document.getElementById('radial-overlay'); var backdrop = document.getElementById('radial-backdrop'); var menu = document.getElementById('radial-menu'); window.openRadialMenu = function() { if (isOpen) return; isOpen = true; activeTouch = null; var svg = document.getElementById('radial-svg'); var w = window.innerWidth; var sz = w < 1024 ? '75vw' : '25vw'; overlay.style.width = sz; overlay.style.height = sz; if (needsOuterRing) { svg.setAttribute('viewBox', '2 2 396 396'); } else { svg.setAttribute('viewBox', '70 70 260 260'); } overlay.classList.remove('hidden'); overlay.style.display = 'flex'; overlay.style.alignItems = 'center'; overlay.style.justifyContent = 'center'; backdrop.classList.remove('hidden'); buildMenu(); requestAnimationFrame(function() { requestAnimationFrame(function() { menu.style.opacity = '1'; menu.style.transform = 'scale(1)'; }); }); }; window.closeRadialMenu = function() { if (!isOpen) return; isOpen = false; activeTouch = null; menu.style.opacity = '0'; menu.style.transform = 'scale(0.4)'; backdrop.classList.add('hidden'); setTimeout(function() { overlay.classList.add('hidden'); overlay.style.display = ''; }, 320); }; overlay.addEventListener('mouseleave', function() { if (window.innerWidth >= 1024) closeRadialMenu(); }); backdrop.addEventListener('click', function() { closeRadialMenu(); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeRadialMenu(); closeUserDropdown(); } }); var dropdownBtn = document.getElementById('user-dropdown-btn'); var dropdownPanel = document.getElementById('user-dropdown-panel'); var dropdownOpen = false; window.toggleUserDropdown = function() { dropdownOpen = !dropdownOpen; if (dropdownOpen) { dropdownPanel.classList.remove('opacity-0','invisible','translate-y-1'); dropdownPanel.classList.add('opacity-100','visible','translate-y-0'); dropdownBtn.setAttribute('aria-expanded','true'); } else closeUserDropdown(); }; function closeUserDropdown() { if (!dropdownOpen) return; dropdownOpen = false; dropdownPanel.classList.remove('opacity-100','visible','translate-y-0'); dropdownPanel.classList.add('opacity-0','invisible','translate-y-1'); dropdownBtn.setAttribute('aria-expanded','false'); } document.addEventListener('click', function(e) { if (dropdownBtn && dropdownPanel) { var w = document.getElementById('user-dropdown-wrapper'); if (w && !w.contains(e.target)) closeUserDropdown(); } }); var mobileUserBtn = document.getElementById('mobile-user-btn'); var mobileUserPanel = document.getElementById('mobile-user-panel'); var mobileUserOpen = false; window.toggleMobileUserMenu = function() { mobileUserOpen = !mobileUserOpen; if (mobileUserOpen) { mobileUserPanel.classList.remove('hidden'); } else { mobileUserPanel.classList.add('hidden'); } }; document.addEventListener('click', function(e) { var w = document.getElementById('mobile-user-wrapper'); if (w && !w.contains(e.target)) { mobileUserOpen = false; if (mobileUserPanel) mobileUserPanel.classList.add('hidden'); } }); var header = document.getElementById('site-header'); function handleScroll() { if (window.scrollY > 10) header.classList.add('shadow-lg'); else header.classList.remove('shadow-lg'); } window.addEventListener('scroll', handleScroll, { passive: true }); handleScroll(); })();

Que ofrecemos

Las mejores soluciones, para la empresa y el particular

Ofrecemos un servicio integral que cubre todas las necesidades de fontanería, electricidad, calefacción y domótica, con profesionalidad y garantía.

01 IDEA

Encuentra los servicios que necesitas de forma rápida y sencilla desde cualquier dispositivo.

02 CONCEPTO

Minimizamos los esfuerzos gestionando todo el proceso de principio a fin por ti.

03 DISENO

Diseñamos soluciones a medida adaptadas a cada cliente y cada situacion.

04 DESARROLLO

Desarrollamos nuevos servicios continuamente para cubrir todas tus necesidades.

05 CALIDAD

Cumplimos los mas altos estándares de calidad en cada intervencion realizada.

Tienda Online

Productos y Servicios

Descubre nuestra gama de productos y servicios disponibles. Servicio tecnico oficial de las mejores marcas.

Cargando productos...