diff --git a/src/factory/components/Feature/index.jsx b/src/factory/components/Feature/index.jsx index 028097274..3b121e07b 100644 --- a/src/factory/components/Feature/index.jsx +++ b/src/factory/components/Feature/index.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState, useRef } from 'react'; import './index.scss'; -import featureLeft1 from '../../../images/factory/feature-left-1.webp'; +import featureLeft1 from '../../../images/factory/feature-left-1.png'; import featureLeft2 from '../../../images/factory/feature-left-2.jpg'; import featureright1 from '../../../images/factory/feature-right-1.webp'; import featureright2 from '../../../images/factory/feature-right-2.jpg'; @@ -53,6 +53,7 @@ function Feature(props) { const getStandard = () => { getSourceList({ id: factoryZoneId, keywords: standardId }).then(res => { const rows = [] + if (!res || !res.data || !res.data.rows) return res.data.rows.forEach(e => { let extendData = {} if (e.extendData) { diff --git a/src/factory/components/Feature/index.scss b/src/factory/components/Feature/index.scss index 99d259bc3..80287d3d5 100644 --- a/src/factory/components/Feature/index.scss +++ b/src/factory/components/Feature/index.scss @@ -104,7 +104,7 @@ background-image: url('../../../images/factory/feature-left-bg2.webp'); img { width: 620px; - margin-top: 32px; + // margin-top: 32px; } } diff --git a/src/factory/components/Resource/index.jsx b/src/factory/components/Resource/index.jsx index 4d3eb2b3c..bf971a683 100644 --- a/src/factory/components/Resource/index.jsx +++ b/src/factory/components/Resource/index.jsx @@ -1,7 +1,8 @@ -import React, { useEffect, useRef, useState, forwardRef } from 'react'; +import React, { useEffect, useRef, useState, forwardRef, useImperativeHandle } from 'react'; import './index.scss'; import gsap from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; +import { ScrollToPlugin } from 'gsap/ScrollToPlugin'; import KFIcon from '../../../components/KFIcon'; import { Link } from 'react-router-dom'; import { getProjectsLists, getSourceList, httpUrl } from '../../api'; @@ -10,7 +11,7 @@ import { downloadFunc } from '../../../forge/Utils/utils' import '../../common.scss' import { Modal } from 'antd'; -gsap.registerPlugin(ScrollTrigger); +gsap.registerPlugin(ScrollTrigger, ScrollToPlugin); const resourceInfo = [ { name: '项目', title: '工厂开源项目', icon: 'icon-xiangmu', desc: '基于标准化生产线开发的项目源码、依赖包源码、标准化生产线源码等', path: '/sf/resources/project' }, @@ -26,6 +27,8 @@ const Resource = forwardRef(({ zoneId }, ref) => { const r4Ref = useRef(null); const containerRef = useRef(null); const timelineRef = useRef(null); + const scrollTriggerRef = useRef(null); + const wrapperRef = useRef(null); // 用于 IntersectionObserver 的内部 ref const [isVisible, setIsVisible] = useState(false); const [resourceList1, setResourceList1] = useState([]); @@ -34,6 +37,14 @@ const Resource = forwardRef(({ zoneId }, ref) => { const [resourceList4, setResourceList4] = useState([]); const [manualList, setManualList] = useState([]); + // 调试参数:调整每个面板的滚动偏移量(单位:像素) + const scrollOffsetConfig = useRef({ + 0: 0, // resource-1 偏移量 + 1: 0, // resource-2 偏移量 + 2: 0, // resource-3 偏移量 + 3: 300, // resource-4 偏移量 + }); + useEffect(() => { if (!zoneId) return; getToolsList(); @@ -55,61 +66,152 @@ const Resource = forwardRef(({ zoneId }, ref) => { } }, { threshold: 0.1 }); - if (ref.current) { - observer.observe(ref.current); + if (wrapperRef.current) { + observer.observe(wrapperRef.current); } return () => observer.disconnect(); }, []); // 初始化GSAP动画 useEffect(() => { - if (!containerRef.current) return; - - // 获取面板元素 - const panels = [r1Ref.current, r2Ref.current, r3Ref.current, r4Ref.current]; - - // 创建时间轴动画 - timelineRef.current = gsap.timeline({ - scrollTrigger: { - trigger: containerRef.current, - start: "top top", - end: "+=800", - scrub: 1, - pin: false, - anticipatePin: 1, - markers: false, + // 检查元素是否在 DOM 中并且有正确的 offsetParent + const checkAndInit = () => { + const container = containerRef.current; + const panels = [r1Ref.current, r2Ref.current, r3Ref.current, r4Ref.current]; + + // 确保所有元素都存在且在 DOM 中有位置信息 + if (!container || !container.offsetParent || panels.some(p => !p || !p.offsetParent)) { + return false; } - }); - // 为每个面板创建动画并添加回调 - panels.forEach((panel, index) => { - const animations = [ - { y: "-5%", scale: 0.85, duration: 1 }, - { y: "-50%", scale: 0.9, duration: 1 }, - { y: "-265%", scale: 0.95, duration: 1.2 }, - { y: "-415%", scale: 0.98, duration: 2 } - ]; + try { + // 创建时间轴动画 + timelineRef.current = gsap.timeline({ + scrollTrigger: { + trigger: container, + start: "top top", + end: "+=800", + scrub: 1, + pin: false, + anticipatePin: 1, + markers: false, + onUpdate: (self) => { + scrollTriggerRef.current = self; + } + } + }); - timelineRef.current.to(panel, { - ...animations[index], - onComplete: () => { - panel.classList.add('animated-panel'); - }, - onReverseComplete: () => { - panel.classList.remove('animated-panel'); - } - }, 0); - }); + // 为每个面板创建动画 + const animations = [ + { y: "-5%", scale: 0.85, duration: 1 }, + { y: "-50%", scale: 0.9, duration: 1 }, + { y: "-265%", scale: 0.95, duration: 1.2 }, + { y: "-415%", scale: 0.98, duration: 2 } + ]; + + panels.forEach((panel, index) => { + if (panel && animations[index]) { + timelineRef.current.to(panel, { + ...animations[index], + onComplete: () => { + panel.classList.add('animated-panel'); + }, + onReverseComplete: () => { + panel.classList.remove('animated-panel'); + } + }, 0); + } + }); + + return true; + } catch (error) { + console.error('GSAP animation init error:', error); + return false; + } + }; + + // 尝试初始化,如果失败则每 100ms 重试一次 + let attempts = 0; + const maxAttempts = 100; // 最多等待 10 秒 + + const tryInit = () => { + if (checkAndInit()) { + console.log('Resource GSAP animation initialized'); + } else if (attempts < maxAttempts) { + attempts++; + setTimeout(tryInit, 100); + } else { + console.error('Failed to initialize Resource GSAP animation'); + } + }; + + // 立即开始尝试 + tryInit(); // 清理函数 return () => { if (timelineRef.current) { timelineRef.current.kill(); } - ScrollTrigger.getAll().forEach(trigger => trigger.kill()); + // 只清理当前组件创建的 ScrollTrigger + ScrollTrigger.getAll().forEach(trigger => { + if (trigger.trigger === containerRef.current) { + trigger.kill(); + } + }); }; }, []); + // 点击面板跳转到对应滚动位置 + // progressValues: 每个面板对应的动画进度值 (0-1) + const progressValues = [0, 0.25, 0.5, 0.75]; + + const handlePanelClick = (index, event) => { + // 如果点击的是子元素(按钮、链接、内容项等),不触发面板滚动 + if (event) { + const target = event.target; + // 检查是否点击了按钮、链接、内容项或它们的子元素 + if (target.closest('button') || + target.closest('a') || + target.closest('.item-btn') || + target.closest('.content-item1') || + target.closest('.content-item2') || + target.closest('.content-item3')) { + return; + } + } + + if (!scrollTriggerRef.current) return; + + const st = scrollTriggerRef.current; + const targetProgress = progressValues[index]; + + // 计算目标滚动位置 + // start 是动画开始的滚动位置,end 是动画结束的滚动位置 + const start = st.start; + const end = st.end; + const targetScroll = start + (end - start) * targetProgress + scrollOffsetConfig.current[index]; + + // 平滑滚动到目标位置 + gsap.to(window, { + scrollTo: { + y: targetScroll, + autoKill: false + }, + duration: 0.8, + ease: "power2.inOut" + }); + }; + + // 暴露方法给父组件(可选) + useImperativeHandle(ref, () => ({ + scrollToPanel: handlePanelClick, + // 获取/设置调试偏移量 + getScrollOffset: (index) => scrollOffsetConfig.current[index], + setScrollOffset: (index, value) => { scrollOffsetConfig.current[index] = value; }, + getAllOffsets: () => ({ ...scrollOffsetConfig.current }), + })); + const getLeftContent = (index) => { const item = resourceInfo[index]; return