修复冲突问题
This commit is contained in:
113
public/js/app.js
113
public/js/app.js
@@ -207,19 +207,20 @@
|
|||||||
dom.btnChangePassword.addEventListener('click', saveChangePassword);
|
dom.btnChangePassword.addEventListener('click', saveChangePassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Globe expansion (FLIP animation)
|
// Globe expansion (FLIP animation via Web Animations API)
|
||||||
let savedGlobeRect = null;
|
let savedGlobeRect = null;
|
||||||
|
let globeAnimating = false;
|
||||||
|
|
||||||
function expandGlobe() {
|
function expandGlobe() {
|
||||||
if (dom.globeCard.classList.contains('expanded')) return;
|
if (dom.globeCard.classList.contains('expanded') || globeAnimating) return;
|
||||||
|
globeAnimating = true;
|
||||||
|
|
||||||
// Save original rect for FLIP
|
// FLIP: capture original position
|
||||||
savedGlobeRect = dom.globeCard.getBoundingClientRect();
|
savedGlobeRect = dom.globeCard.getBoundingClientRect();
|
||||||
|
|
||||||
|
// Apply expanded state
|
||||||
dom.globeCard.classList.add('expanded');
|
dom.globeCard.classList.add('expanded');
|
||||||
dom.btnExpandGlobe.classList.add('active');
|
dom.btnExpandGlobe.classList.add('active');
|
||||||
|
|
||||||
// Update button icon/title
|
|
||||||
dom.btnExpandGlobe.title = '缩小显示';
|
dom.btnExpandGlobe.title = '缩小显示';
|
||||||
dom.btnExpandGlobe.innerHTML = `
|
dom.btnExpandGlobe.innerHTML = `
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 18px; height: 18px;">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 18px; height: 18px;">
|
||||||
@@ -227,59 +228,53 @@
|
|||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Resize ECharts immediately (prevents flash)
|
||||||
if (myMap2D) myMap2D.resize();
|
if (myMap2D) myMap2D.resize();
|
||||||
|
|
||||||
|
// FLIP: capture expanded position
|
||||||
const endRect = dom.globeCard.getBoundingClientRect();
|
const endRect = dom.globeCard.getBoundingClientRect();
|
||||||
const scaleX = savedGlobeRect.width / endRect.width;
|
const scaleX = savedGlobeRect.width / endRect.width;
|
||||||
const scaleY = savedGlobeRect.height / endRect.height;
|
const scaleY = savedGlobeRect.height / endRect.height;
|
||||||
|
|
||||||
// Using top-left for math (transformOrigin: 0 0)
|
|
||||||
const dx = savedGlobeRect.left - endRect.left;
|
const dx = savedGlobeRect.left - endRect.left;
|
||||||
const dy = savedGlobeRect.top - endRect.top;
|
const dy = savedGlobeRect.top - endRect.top;
|
||||||
|
|
||||||
dom.globeCard.style.willChange = 'transform, box-shadow';
|
// Animate using Web Animations API (bypasses all CSS conflicts)
|
||||||
dom.globeCard.style.transformOrigin = '0 0';
|
const anim = dom.globeCard.animate([
|
||||||
dom.globeCard.style.transition = 'none';
|
{
|
||||||
dom.globeCard.style.transform = `translate(${dx}px, ${dy}px) scale(${scaleX}, ${scaleY})`;
|
transform: `translate(${dx}px, ${dy}px) scale(${scaleX}, ${scaleY})`,
|
||||||
dom.globeCard.style.boxShadow = '0 0 0 0 transparent, 0 0 0 0 transparent';
|
boxShadow: '0 0 0 0 transparent, 0 0 0 0 transparent',
|
||||||
|
offset: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: 'translate(0, 0) scale(1)',
|
||||||
|
boxShadow: '0 0 80px rgba(0,0,0,0.8), 0 0 0 100vh rgba(0,0,0,0.65)',
|
||||||
|
offset: 1
|
||||||
|
}
|
||||||
|
], {
|
||||||
|
duration: 400,
|
||||||
|
easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
||||||
|
fill: 'none'
|
||||||
|
});
|
||||||
|
|
||||||
dom.globeCard.offsetHeight; // Force reflow
|
anim.onfinish = () => {
|
||||||
|
globeAnimating = false;
|
||||||
dom.globeCard.style.transition = 'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s ease';
|
|
||||||
dom.globeCard.style.transform = 'translate(0px, 0px) scale(1)';
|
|
||||||
dom.globeCard.style.boxShadow = '';
|
|
||||||
|
|
||||||
const onTransitionEnd = (e) => {
|
|
||||||
if (e.propertyName !== 'transform') return;
|
|
||||||
dom.globeCard.removeEventListener('transitionend', onTransitionEnd);
|
|
||||||
dom.globeCard.style.transition = '';
|
|
||||||
dom.globeCard.style.willChange = '';
|
|
||||||
dom.globeCard.style.transformOrigin = '';
|
|
||||||
dom.globeCard.style.transform = '';
|
|
||||||
};
|
};
|
||||||
dom.globeCard.addEventListener('transitionend', onTransitionEnd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function collapseGlobe() {
|
function collapseGlobe() {
|
||||||
if (!dom.globeCard.classList.contains('expanded')) return;
|
if (!dom.globeCard.classList.contains('expanded') || globeAnimating) return;
|
||||||
if (dom.globeCard.classList.contains('globe-collapsing')) return;
|
globeAnimating = true;
|
||||||
|
|
||||||
const resetState = () => {
|
const resetState = () => {
|
||||||
dom.globeCard.classList.remove('expanded', 'globe-collapsing');
|
dom.globeCard.classList.remove('expanded', 'globe-collapsing');
|
||||||
dom.btnExpandGlobe.classList.remove('active');
|
dom.btnExpandGlobe.classList.remove('active');
|
||||||
dom.globeCard.style.transition = '';
|
|
||||||
dom.globeCard.style.transform = '';
|
|
||||||
dom.globeCard.style.boxShadow = '';
|
|
||||||
dom.globeCard.style.willChange = '';
|
|
||||||
|
|
||||||
// Restore button icon/title
|
|
||||||
dom.btnExpandGlobe.title = '放大显示';
|
dom.btnExpandGlobe.title = '放大显示';
|
||||||
dom.btnExpandGlobe.innerHTML = `
|
dom.btnExpandGlobe.innerHTML = `
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 18px; height: 18px;">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 18px; height: 18px;">
|
||||||
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" />
|
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
|
globeAnimating = false;
|
||||||
if (myMap2D) requestAnimationFrame(() => myMap2D.resize());
|
if (myMap2D) requestAnimationFrame(() => myMap2D.resize());
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -290,43 +285,35 @@
|
|||||||
|
|
||||||
dom.globeCard.classList.add('globe-collapsing');
|
dom.globeCard.classList.add('globe-collapsing');
|
||||||
|
|
||||||
|
// FLIP: compute target transform to original position
|
||||||
const expandedRect = dom.globeCard.getBoundingClientRect();
|
const expandedRect = dom.globeCard.getBoundingClientRect();
|
||||||
const scaleX = savedGlobeRect.width / expandedRect.width;
|
const scaleX = savedGlobeRect.width / expandedRect.width;
|
||||||
const scaleY = savedGlobeRect.height / expandedRect.height;
|
const scaleY = savedGlobeRect.height / expandedRect.height;
|
||||||
|
|
||||||
// Match origin used in expand
|
|
||||||
const dx = savedGlobeRect.left - expandedRect.left;
|
const dx = savedGlobeRect.left - expandedRect.left;
|
||||||
const dy = savedGlobeRect.top - expandedRect.top;
|
const dy = savedGlobeRect.top - expandedRect.top;
|
||||||
|
|
||||||
// Force setup the expanded state with origin
|
// Animate from current expanded state to original rect
|
||||||
dom.globeCard.style.transformOrigin = '0 0';
|
const anim = dom.globeCard.animate([
|
||||||
dom.globeCard.style.transform = 'translate(0px, 0px) scale(1)';
|
{
|
||||||
dom.globeCard.style.boxShadow = ''; // Reset to what CSS says
|
transform: 'translate(0, 0) scale(1)',
|
||||||
dom.globeCard.offsetHeight; // Critical sync reflow
|
boxShadow: '0 0 80px rgba(0,0,0,0.8), 0 0 0 100vh rgba(0,0,0,0.65)',
|
||||||
|
offset: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: `translate(${dx}px, ${dy}px) scale(${scaleX}, ${scaleY})`,
|
||||||
|
boxShadow: '0 0 0 0 transparent, 0 0 0 0 transparent',
|
||||||
|
offset: 1
|
||||||
|
}
|
||||||
|
], {
|
||||||
|
duration: 350,
|
||||||
|
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
fill: 'forwards' // Hold final frame until we remove class
|
||||||
|
});
|
||||||
|
|
||||||
dom.globeCard.style.willChange = 'transform, box-shadow';
|
anim.onfinish = () => {
|
||||||
dom.globeCard.style.transition = 'transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.35s ease';
|
anim.cancel(); // Release the fill-forwards hold
|
||||||
|
|
||||||
// Apply target values immediately after reflow
|
|
||||||
dom.globeCard.style.transform = `translate(${dx}px, ${dy}px) scale(${scaleX}, ${scaleY})`;
|
|
||||||
dom.globeCard.style.boxShadow = '0 0 0 0 transparent, 0 0 0 0 transparent';
|
|
||||||
|
|
||||||
let transitionFinished = false;
|
|
||||||
const onTransitionEnd = (e) => {
|
|
||||||
if (e.propertyName !== 'transform') return;
|
|
||||||
transitionFinished = true;
|
|
||||||
dom.globeCard.removeEventListener('transitionend', onTransitionEnd);
|
|
||||||
resetState();
|
resetState();
|
||||||
};
|
};
|
||||||
dom.globeCard.addEventListener('transitionend', onTransitionEnd);
|
|
||||||
|
|
||||||
// Robustness fallback
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!transitionFinished) {
|
|
||||||
dom.globeCard.removeEventListener('transitionend', onTransitionEnd);
|
|
||||||
resetState();
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dom.btnExpandGlobe) {
|
if (dom.btnExpandGlobe) {
|
||||||
|
|||||||
Reference in New Issue
Block a user