如何将public文件夹内的bg.html作为组件引入React的HeroHome.jsx
将bg.html动画作为背景引入React组件的实现方案
步骤1:迁移并适配资源
- 把bg.html关联的
style.css、script.js复制到React项目的src/assets目录下,将style.css重命名为bg-animation.css以便区分。 - 替换CDN依赖,用npm安装所需库:
npm install gsap lodash
步骤2:创建BgAnimation React组件
新建src/components/BgAnimation.jsx,将原bg.html的动画逻辑转换为React组件:
import React, { useEffect, useRef } from 'react'; import gsap from 'gsap'; import _ from 'lodash'; import '../assets/bg-animation.css'; function BgAnimation() { const containerRef = useRef(null); useEffect(() => { // 复制原script.js中的动画逻辑,适配React环境 const canvas = document.createElement('canvas'); containerRef.current.appendChild(canvas); // 这里粘贴原script.js的核心动画代码,注意将DOM选择器替换为ref相关操作 // 示例(对应原"Jump to Hyperspace"逻辑): const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; // 原动画的初始化、帧循环等代码放到这里 // 窗口 resize 适配 const handleResize = _.debounce(() => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }, 100); window.addEventListener('resize', handleResize); // 清理函数 return () => { gsap.killTweensOf('*'); window.removeEventListener('resize', handleResize); if (canvas) containerRef.current.removeChild(canvas); }; }, []); return <div ref={containerRef} className="bg-animation-container"></div>; } export default BgAnimation;
在bg-animation.css中添加容器样式:
.bg-animation-container { width: 100%; height: 100%; overflow: hidden; }
步骤3:在HeroHome.jsx中引入并使用背景组件
修改HeroHome.jsx,将BgAnimation作为底层背景插入:
import React, { useState } from 'react'; import Modal from '../utils/Modal'; import HeroImage from '../images/hero-image.png'; import BgAnimation from './BgAnimation'; function HeroHome() { const [videoModalOpen, setVideoModalOpen] = useState(false); return ( <section className="relative min-h-screen"> {/* 背景动画层 - 置于最底层,不干扰内容交互 */} <div className="absolute inset-0 pointer-events-none"> <BgAnimation /> </div> {/* 原有的Illustration元素(按需保留/移除) */} <div className="absolute left-1/2 transform -translate-x-1/2 bottom-0 pointer-events-none" aria-hidden="true"> <svg width="1360" height="578" viewBox="0 0 1360 578" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="illustration-01"> <stop stopColor="#FFF" offset="0%" /> <stop stopColor="#EAEAEA" offset="77.402%" /> <stop stopColor="#DFDFDF" offset="100%" /> </linearGradient> </defs> <g fill="url(#illustration-01)" fillRule="evenodd"> <circle cx="1232" cy="128" r="128" /> <circle cx="155" cy="443" r="64" /> </g> </svg> </div> {/* 内容容器 - 提升层级确保在背景之上 */} <div className="max-w-6xl mx-auto px-4 sm:px-6 relative z-10"> <div className="pt-32 pb-12 md:pt-40 md:pb-20"> <div className="text-center pb-12 md:pb-16"> <h1 className="text-5xl md:text-6xl font-extrabold leading-tighter tracking-tighter mb-4" data-aos="zoom-y-out">Make your website <span className="bg-clip-text text-transparent bg-gradient-to-r from-blue-500 to-teal-400">wonderful</span></h1> <div className="max-w-3xl mx-auto"> <p className="text-xl text-gray-600 mb-8" data-aos="zoom-y-out" data-aos-delay="150">Our landing page template works on all devices, so you only have to set it up once, and get beautiful results forever.</p> <div className="max-w-xs mx-auto sm:max-w-none sm:flex sm:justify-center" data-aos="zoom-y-out" data-aos-delay="300"> <div> <a className="btn text-white bg-blue-600 hover:bg-blue-700 w-full mb-4 sm:w-auto sm:mb-0" href="#0">Start free trial</a> </div> <div> <a className="btn text-white bg-gray-900 hover:bg-gray-800 w-full sm:w-auto sm:ml-4" href="#0">Learn more</a> </div> </div> </div> </div> <div> <div className="relative flex justify-center mb-8" data-aos="zoom-y-out" data-aos-delay="450"> <div className="flex flex-col justify-center"> <img className="mx-auto" src={HeroImage} width="768" height="432" alt="Hero" /> <svg className="absolute inset-0 max-w-full mx-auto md:max-w-none h-auto" width="768" height="432" viewBox="0 0 768 432" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="hero-ill-a"> <stop stopColor="#FFF" offset="0%" /> <stop stopColor="#EAEAEA" offset="77.402%" /> <stop stopColor="#DFDFDF" offset="100%" /> </linearGradient> <linearGradient x1="50%" y1="0%" x2="50%" y2="99.24%" id="hero-ill-b"> <stop stopColor="#FFF" offset="0%" /> <stop stopColor="#EAEAEA" offset="48.57%" /> <stop stopColor="#DFDFDF" stopOpacity="0" offset="100%" /> </linearGradient> <radialGradient cx="21.152%" cy="86.063%" fx="21.152%" fy="86.063%" r="79.941%" id="hero-ill-e"> <stop stopColor="#4FD1C5" offset="0%" /> <stop stopColor="#81E6D9" offset="25.871%" /> <stop stopColor="#338CF5" offset="100%" /> </radialGradient> <circle id="hero-ill-d" cx="384" cy="216" r="64" /> </defs> <g fill="none" fillRule="evenodd"> <circle fillOpacity=".04" fill="url(#hero-ill-a)" cx="384" cy="216" r="128" /> <circle fillOpacity=".16" fill="url(#hero-ill-b)" cx="384" cy="216" r="96" /> <g fillRule="nonzero"> <use fill="#000" xlinkHref="#hero-ill-d" /> <use fill="url(#hero-ill-e)" xlinkHref="#hero-ill-d" /> </g> </g> </svg> </div> <button className="absolute top-full flex items-center transform -translate-y-1/2 bg-white rounded-full font-medium group p-4 shadow-lg" onClick={(e) => { e.preventDefault(); e.stopPropagation(); setVideoModalOpen(true); }} aria-controls="modal"> <svg className="w-6 h-6 fill-current text-gray-400 group-hover:text-blue-600 flex-shrink-0" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zm0 2C5.373 24 0 18.627 0 12S5.373 0 12 0s12 5.373 12 12-5.373 12-12 12z" /> <path d="M10 17l6-5-6-5z" /> </svg> <span className="ml-3">Watch the full video (2 min)</span> </button> </div> <Modal id="modal" ariaLabel="modal-headline" show={videoModalOpen} handleClose={() => setVideoModalOpen(false)}> <div className="relative pb-9/16"> <iframe className="absolute w-full h-full" src="https://player.vimeo.com/video/174002812" title="Video" allowFullScreen></iframe> </div> </Modal> </div> </div> </div> </section> ); } export default HeroHome;
关键注意事项
- 原
script.js中的DOM操作必须替换为React ref调用,避免直接操作document导致的虚拟DOM冲突。 - 动画的事件监听(如鼠标/触摸事件)需在
useEffect中注册,并在清理函数中移除,防止内存泄漏。 - 通过
pointer-events: none确保背景动画不干扰上层内容的交互,z-10保证内容层级高于背景。
内容的提问来源于stack exchange,提问作者user20659824
相关产品推荐
相关产品推荐

