This is an automated email from the ASF dual-hosted git repository.

bzp2010 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-website.git


The following commit(s) were added to refs/heads/master by this push:
     new 9b95d48a4ae refactor: optimize landing page (#1170)
9b95d48a4ae is described below

commit 9b95d48a4ae8d657b0d48ddfe5a01ab31e26a8cd
Author: Young <[email protected]>
AuthorDate: Wed Jun 29 18:14:29 2022 +0800

    refactor: optimize landing page (#1170)
---
 package.json                                       |   3 +-
 website/config/ssrTemplate.js                      |  10 +-
 website/i18n/zh/code.json                          |   9 +-
 website/src/components/HeroCanvas.tsx              |  56 ++-
 website/src/components/OssCanvas.tsx               | 142 ------
 website/src/components/sections/Features.tsx       | 513 ++++++++++-----------
 website/src/components/sections/HeroSection.tsx    |  24 +-
 .../src/components/sections/OpensourcePromo.tsx    |  35 +-
 website/src/css/customTheme.css                    |  43 +-
 9 files changed, 360 insertions(+), 475 deletions(-)

diff --git a/package.json b/package.json
index 236af8760a5..1eeda13fa22 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
     "update-sitemap": "yarn workspace scripts update-sitemap",
     "start": "yarn workspace website start",
     "build": "yarn workspace website build",
+    "preview-build": "preview=true yarn workspace website build",
     "docusaurus": "yarn workspace website docusaurus",
     "serve": "yarn docusaurus serve",
     "prepare": "husky install",
@@ -38,4 +39,4 @@
     "*.{yml,yaml}": "eslint --cache --fix",
     "*.css": "stylelint --cache --fix"
   }
-}
\ No newline at end of file
+}
diff --git a/website/config/ssrTemplate.js b/website/config/ssrTemplate.js
index ba9c8e3f42d..1cba3d83886 100644
--- a/website/config/ssrTemplate.js
+++ b/website/config/ssrTemplate.js
@@ -13,11 +13,11 @@ module.exports = {
       <% it.metaAttributes.forEach((metaAttribute) => { %>
         <%~ metaAttribute %>
       <% }); %>
-      <% it.stylesheets.forEach((stylesheet) => { %>
-        <link rel="stylesheet" 
href="https://apisix-website-static.apiseven.com<%= it.baseUrl %><%= stylesheet 
%>" />
-      <% }); %>
       <% it.scripts.forEach((script) => { %>
-        <link rel="preload" 
href="https://apisix-website-static.apiseven.com<%= it.baseUrl %><%= script %>" 
as="script">
+        <link rel="preload" href="<%= process.env.preview ? '' : 
'https://apisix-website-static.apiseven.com' %><%= it.baseUrl %><%= script %>" 
as="script">
+      <% }); %>
+      <% it.stylesheets.forEach((stylesheet) => { %>
+        <link rel="stylesheet" href="<%= process.env.preview ? '' : 
'https://apisix-website-static.apiseven.com' %><%= it.baseUrl %><%= stylesheet 
%>" />
       <% }); %>
       <!-- Matomo from the ASF -->
       <script>
@@ -45,7 +45,7 @@ module.exports = {
         <%~ it.appHtml %>
       </div>
       <% it.scripts.forEach((script) => { %>
-        <script src="https://apisix-website-static.apiseven.com<%= it.baseUrl 
%><%= script %>"></script>
+        <script src="<%= process.env.preview ? '' : 
'https://apisix-website-static.apiseven.com' %><%= it.baseUrl %><%= script 
%>"></script>
       <% }); %>
       <%~ it.postBodyTags %>
     </body>
diff --git a/website/i18n/zh/code.json b/website/i18n/zh/code.json
index 933de43ebe0..335a37e42e8 100644
--- a/website/i18n/zh/code.json
+++ b/website/i18n/zh/code.json
@@ -291,8 +291,11 @@
   "openSourcePromo.component.ossPromo.title": {
     "message": "为 Apache APISIX 贡献你的力量!"
   },
-  "openSourcePromo.component.ossPromo.subtitle": {
-    "message": "Apache APISIX 完全开放源码并在不断发展!我们欢迎任何形式的贡献,请在 GitHub 上与社区互动。"
+  "openSourcePromo.component.ossPromo.subtitle1": {
+    "message": "Apache APISIX 完全开放源码并在不断发展!"
+  },
+  "openSourcePromo.component.ossPromo.subtitle2": {
+    "message": "我们欢迎任何形式的贡献,请在 GitHub 上与社区互动。"
   },
   "openSourcePromo.component.link.Github": {
     "message": "立即访问",
@@ -331,4 +334,4 @@
     "message": "立即体验",
     "description": "Download"
   }
-}
\ No newline at end of file
+}
diff --git a/website/src/components/HeroCanvas.tsx 
b/website/src/components/HeroCanvas.tsx
index f1b9b45389c..d9c2c88df3d 100644
--- a/website/src/components/HeroCanvas.tsx
+++ b/website/src/components/HeroCanvas.tsx
@@ -1,7 +1,20 @@
 import type { FC } from 'react';
 import React, { useRef, useEffect } from 'react';
-import * as THREE from 'three';
-import gsap from 'gsap';
+import {
+  Vector2,
+  Color,
+  ShaderMaterial,
+  Points,
+  PlaneBufferGeometry,
+  Mesh,
+  MeshBasicMaterial,
+  WebGLRenderer,
+  PerspectiveCamera,
+  BufferAttribute,
+  Scene,
+  DoubleSide,
+} from 'three';
+import { gsap } from 'gsap/gsap-core';
 
 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
 
@@ -61,7 +74,8 @@ const HeroCanvas: FC = () => {
       ctx.x = ((ctx.x - canvasOffset.left) / canvasWidth);
       ctx.y = ((ctx.y - canvasOffset.top) / canvasHeight);
 
-      gsap.to(mouse, 2, {
+      gsap.to(mouse, {
+        duration: 2,
         x: ctx.x * (canvasWidth / canvasHeight) - (canvasWidth / canvasHeight) 
/ 2,
         y: (1.0 - ctx.y) - 0.5,
         onUpdate: () => {
@@ -70,7 +84,8 @@ const HeroCanvas: FC = () => {
         },
       });
 
-      gsap.to(fragMouse, 2, {
+      gsap.to(fragMouse, {
+        duration: 2,
         x: ctx.x,
         y: (1.0 - ctx.y),
         onUpdate: () => {
@@ -113,20 +128,20 @@ const HeroCanvas: FC = () => {
     function init(width: number, height: number) {
       const ctx = canvasRef.current;
 
-      renderer = new THREE.WebGLRenderer({ canvas: ctx });
+      renderer = new WebGLRenderer({ canvas: ctx });
       renderer.autoClearColor = false;
 
-      camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100);
+      camera = new PerspectiveCamera(45, width / height, 0.1, 100);
       controls = new OrbitControls(camera, renderer.domElement);
 
       controls.enableZoom = false;
       controls.enablePan = false;
       controls.enabled = false;
 
-      geometry = new THREE.PlaneBufferGeometry(width / height, 1, 250, 250);
+      geometry = new PlaneBufferGeometry(width / height, 1, 250, 250);
 
       const { count } = geometry.attributes.position;
-      const arrSize = new THREE.BufferAttribute(new Float32Array(count), 1);
+      const arrSize = new BufferAttribute(new Float32Array(count), 1);
 
       for (let i = 0; i < arrSize.count; i += 1) {
         arrSize.array[i] = getRandom(0, 1);
@@ -135,7 +150,7 @@ const HeroCanvas: FC = () => {
 
       geometry.scale(2.0, 1.0, 1.0);
 
-      scene = new THREE.Scene();
+      scene = new Scene();
       renderer.setSize(canvasWidth, canvasHeight);
 
       const uniforms = {
@@ -145,36 +160,36 @@ const HeroCanvas: FC = () => {
         },
         u_resolution: {
           type: 'v2',
-          value: new THREE.Vector2(),
+          value: new Vector2(),
         },
         u_mouse: {
           type: 'v2',
-          value: new THREE.Vector2(0.5, 0.5),
+          value: new Vector2(0.5, 0.5),
         },
         u_fragMouse: {
           type: 'v2',
-          value: new THREE.Vector2(0.5, 0.5),
+          value: new Vector2(0.5, 0.5),
         },
       };
 
-      scene.background = new THREE.Color('red');
+      scene.background = new Color('red');
 
       camera.position.z = 5;
       controls.update();
 
-      material = new THREE.ShaderMaterial({
+      material = new ShaderMaterial({
         uniforms,
         vertexShader: vertex,
         fragmentShader: fragment,
         wireframe: true,
-        side: THREE.DoubleSide,
+        side: DoubleSide,
       });
 
-      mesh = new THREE.Points(geometry, material);
+      mesh = new Points(geometry, material);
 
-      const backGeometry = new THREE.PlaneBufferGeometry(width / height, 1, 
200, 200);
-      const bgMaterial = new THREE.MeshBasicMaterial({ color: 0x121212, 
wireframe: false });
-      const background = new THREE.Mesh(backGeometry, bgMaterial);
+      const backGeometry = new PlaneBufferGeometry(width / height, 1, 200, 
200);
+      const bgMaterial = new MeshBasicMaterial({ color: 0x121212, wireframe: 
false });
+      const background = new Mesh(backGeometry, bgMaterial);
 
       backGeometry.scale(50, 50, 1);
       background.position.set(10, 10, -10);
@@ -233,8 +248,7 @@ const HeroCanvas: FC = () => {
   useEffect(() => {
     gsap.to(overlayRef.current, {
       height: 0,
-      delay: window.innerWidth >= 768 ? 0 : 0.3,
-      duration: 2.1,
+      duration: 2,
       ease: 'Expo.easeInOut',
     });
   }, []);
diff --git a/website/src/components/OssCanvas.tsx 
b/website/src/components/OssCanvas.tsx
deleted file mode 100644
index e2d0bd015ef..00000000000
--- a/website/src/components/OssCanvas.tsx
+++ /dev/null
@@ -1,142 +0,0 @@
-import type { FC } from 'react';
-import React, { useRef, useEffect } from 'react';
-import * as THREE from 'three';
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
-
-import '../css/customTheme.css';
-
-const OssCanvas: FC = () => {
-  let screenWidth: number;
-  let canvasHeight: number;
-  let canvasWidth: number;
-
-  const canvasRef = useRef(null);
-
-  useEffect(() => {
-    let camera;
-    let scene;
-    let renderer;
-    let material;
-    let mesh;
-
-    screenWidth = window.innerWidth;
-    window.addEventListener('resize', onWindowResize, false);
-
-    let controls;
-    let isLoaded = false; let isRendering = false; let
-      animationFrame;
-
-    if (screenWidth > 1100) {
-      canvasHeight = 500;
-      canvasWidth = screenWidth / 2;
-    } else {
-      canvasHeight = 500;
-      canvasWidth = screenWidth;
-    }
-
-    function onWindowResize() {
-      screenWidth = window.innerWidth;
-
-      if (screenWidth > 1100) {
-        canvasHeight = 500;
-        canvasWidth = screenWidth / 2;
-      } else {
-        canvasHeight = 500;
-        canvasWidth = screenWidth;
-      }
-
-      renderer.setSize(canvasWidth, canvasHeight);
-    }
-
-    const ossCanvasObserver = new 
IntersectionObserver(onOssCanvasIntersection, {
-      root: null,
-      threshold: 0.01,
-    });
-
-    function init(width, height) {
-      const ctx = canvasRef.current;
-      renderer = new THREE.WebGLRenderer({ canvas: ctx });
-
-      camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 100);
-
-      controls = new OrbitControls(camera, renderer.domElement);
-      controls.enableZoom = false;
-
-      const radius = window.innerWidth > 768 ? 5 : 4.5;
-      const detail = 8;
-
-      const geometry = new THREE.IcosahedronGeometry(radius, detail);
-
-      camera.position.z = 2;
-      camera.position.x = 3;
-
-      controls.update();
-
-      scene = new THREE.Scene();
-      renderer.setSize(canvasWidth, canvasHeight);
-
-      scene.background = new THREE.Color(0x000000);
-
-      material = new THREE.MeshNormalMaterial({ wireframe: false, flatShading: 
true });
-
-      mesh = new THREE.Mesh(geometry, material);
-
-      scene.add(mesh);
-      mesh.position.set(3, 0, -9.5);
-
-      controls.target.copy(mesh.position);
-
-      controls.update();
-      renderer.setPixelRatio(window.devicePixelRatio);
-
-      onWindowResize();
-
-      isLoaded = true;
-    }
-
-    function animate() {
-      animationFrame = requestAnimationFrame(animate);
-
-      mesh.rotation.x += 0.005;
-      mesh.rotation.y += 0.005;
-
-      controls.update();
-
-      renderer.render(scene, camera);
-      isRendering = true;
-    }
-
-    init(canvasWidth, canvasHeight);
-
-    function onOssCanvasIntersection(entries) {
-      entries.forEach((entry) => {
-        if (entry.isIntersecting && isLoaded) {
-          if (isLoaded && !isRendering) {
-            animate();
-          } else {
-            console.log('Loading');
-          }
-        } else {
-          if (animationFrame) {
-            cancelAnimationFrame(animationFrame);
-            isRendering = false;
-          }
-        }
-      });
-    }
-
-    ossCanvasObserver.observe(canvasRef.current);
-
-    return () => {
-      // eslint-disable-next-line prefer-spread
-      scene.remove.apply(scene, scene.children);
-      ossCanvasObserver.disconnect();
-    };
-  }, []);
-
-  return (
-    <canvas ref={canvasRef} width={canvasWidth} height={canvasHeight} 
className="ossCanvas" />
-  );
-};
-
-export default OssCanvas;
diff --git a/website/src/components/sections/Features.tsx 
b/website/src/components/sections/Features.tsx
index 1f8a4bb24dd..dbffbbcdb7c 100644
--- a/website/src/components/sections/Features.tsx
+++ b/website/src/components/sections/Features.tsx
@@ -1,11 +1,12 @@
 import type { FC } from 'react';
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useRef, memo } from 'react';
 import Link from '@docusaurus/Link';
 import useBaseUrl from '@docusaurus/useBaseUrl';
-import gsap from 'gsap';
+import { gsap } from 'gsap/gsap-core';
 import { ScrollTrigger } from 'gsap/ScrollTrigger';
 
 import Translate from '@docusaurus/Translate';
+import useWindowType from '@theme/hooks/useWindowSize';
 import ArrowAnim from '../ArrowAnim';
 
 import '../../css/customTheme.css';
@@ -34,253 +35,57 @@ const DashboardPlayground = () => (
   </Link>
 );
 
-const Features: FC = () => {
+const FeatDesktop: FC = () => {
   const dashboardDiv = useRef(null);
   const userfDiv = useRef(null);
   const pluginDiv = useRef(null);
   const triggerDiv = useRef(null);
-  const triggerDivCol = useRef(null);
   const pinDiv = useRef(null);
   const img1 = useRef(null);
-  const img1col = useRef(null);
   const img2 = useRef(null);
-  const img2col = useRef(null);
   const img3 = useRef(null);
-  const img3col = useRef(null);
-  const featPin = useRef(null);
-
-  gsap.registerPlugin(ScrollTrigger);
-
-  const [screenWidth, setScreenWidth] = useState(
-    typeof window !== 'undefined' && window.innerWidth,
-  );
-  const [, setScreenHeight] = useState(
-    typeof window !== 'undefined' && window.innerWidth,
-  );
-
-  useEffect(() => {
-    setScreenHeight(window.innerHeight);
-    setScreenWidth(window.innerWidth);
-    window.addEventListener('resize', resizeEvent, false);
-
-    function resizeEvent() {
-      setScreenHeight(window.innerHeight);
-      setScreenWidth(window.innerWidth);
-    }
-
-    return () => {
-      window.removeEventListener('resize', resizeEvent);
-    };
-  }, []);
 
   useEffect(() => {
     const value = window.innerHeight * 2;
 
-    let tl;
-    const observers = [];
+    const tl = gsap.timeline({
+      defaults: {
+        ease: 'linear',
+      },
+      scrollTrigger: {
+        id: 'feat',
+        trigger: triggerDiv.current,
+        start: 'top top',
+        pin: pinDiv.current,
+        scrub: 1.5,
+        end: `${value}px`,
+      },
+    });
 
-    if (screenWidth > 1100) {
-      tl = gsap.timeline({
-        defaults: {
-          ease: 'linear',
-        },
-        scrollTrigger: {
-          id: 'feat',
-          trigger: triggerDiv.current,
-          start: 'top top',
-          pin: pinDiv.current,
-          scrub: 1.5,
-          end: `${value}px`,
-        },
-      });
-
-      tl.to(img1.current, {
+    tl.to(img1.current, {
+      opacity: 0,
+    })
+      .to(img2.current, {
+        opacity: 1,
+      })
+      .to(img2.current, {
         opacity: 0,
       })
-        .to(img2.current, {
-          opacity: 1,
-        })
-        .to(img2.current, {
-          opacity: 0,
-        })
-        .to(img3.current, {
-          opacity: 1,
-        });
-    } else {
-      // Mobile
-
-      const elems = [img1col.current, img2col.current, img3col.current];
-      for (let i = 1; i < 4; i += 1) {
-        observers.push(
-          new IntersectionObserver(
-            (entries) => {
-              entries.forEach((entry) => {
-                if (entry.isIntersecting) {
-                  observers[i - 1].disconnect();
-                  gsap.fromTo(
-                    elems[i - 1],
-                    {
-                      opacity: 0,
-                      y: 90,
-                    },
-                    {
-                      opacity: 1,
-                      y: 0,
-                      duration: 0.5,
-                      ease: 'power3.out',
-                      yoyo: true,
-                      yoyoEase: 'power3.inOut',
-                    },
-                  );
-                }
-              });
-            },
-            {
-              root: null,
-              threshold: 0.2,
-            },
-          ),
-        );
-      }
-
-      observers.forEach((it, index) => {
-        it.observe(elems[index]);
+      .to(img3.current, {
+        opacity: 1,
       });
-    }
   });
-
   return (
-    <>
-      <div ref={featPin} className="feat-top" style={{ padding: '50px 0' }}>
-        <h3 className="feat-head-desc"><Translate 
id="features.component.why.title">Why APISIX Gateway?</Translate></h3>
-        <h2 className="feat-head add-left-margin">
-          <Translate id="features.component.why.subtitle">
-            Reduce time fighting bugs, focus on designing world-class systems 
with API Gateway
-          </Translate>
-        </h2>
-        <p className="feat-desc add-left-margin">
-          <Translate id="features.component.why.message">
-            Apache APISIX is the first open-source API Gateway
-            that includes a built-in low-code Dashboard,
-            which offers a powerful and flexible UI for developers to use.
-          </Translate>
-        </p>
-      </div>
-      <div className="feat-container-d" ref={triggerDiv}>
-        {/* Desktop */}
-        <div className="left-pane" style={{ width: '50%', height: '100%' }}>
-          <div ref={dashboardDiv} style={{ position: 'relative' }}>
-            <div className="text-div" style={{ height: '100vh' }}>
-              <h2 className="i-text add-left-margin-feat">
-                <Translate id="features.component.easyDashboard.title">
-                  Easy-to-use Dashboard
-                </Translate>
-              </h2>
-              <p className="i-text-desc add-left-margin-feat">
-                <Translate id="features.component.easyDashboard.message">
-                  The Apache APISIX Dashboard is designed to make it as easy as
-                  possible for users to operate Apache APISIX through a 
frontend
-                  interface. It’s opensource and ever evolving, feel free to
-                  contribute.
-                </Translate>
-              </p>
-              <div className="hero-ctas add-left-margin-feat bottom-pos">
-                <Link to={useBaseUrl('downloads')} className="btn 
btn-download">
-                  <Translate 
id="features.component.easyDashboard.downloadBtn">Downloads</Translate>
-                </Link>
-                <ArrowAnim />
-              </div>
-              <DashboardPlayground />
-            </div>
-          </div>
-
-          <div ref={userfDiv} style={{ position: 'relative' }}>
-            <div className="text-div" style={{ height: '100vh' }}>
-              <h2 className="i-text add-left-margin-feat">
-                <Translate id="features.component.userFlexible.title">User 
Flexible</Translate>
-              </h2>
-              <p className="i-text-desc add-left-margin-feat">
-                <Translate id="features.component.userFlexible.message">
-                  The Apache APISIX dashboard is flexible to User demand,
-                  providing option to create custom modules through code 
matching
-                  your requirements, alongside the existing no-code toolchain.
-                </Translate>
-              </p>
-            </div>
-          </div>
-
-          <div ref={pluginDiv} style={{ position: 'relative' }}>
-            <div className="text-div" style={{ height: '100vh' }}>
-              <h2 className="i-text add-left-margin-feat">
-                <Translate id="features.component.pluginised.title">Pluginised 
Workflow</Translate>
-              </h2>
-              <p className="i-text-desc add-left-margin-feat">
-                <Translate id="features.component.pluginised.message">
-                  No need to reinvent the wheel again and again. Use inbuilt
-                  plugins to create high performance systems in tight 
deadlines.
-                  For something custom, there is option of building custom
-                  plugins.
-                </Translate>
-              </p>
-            </div>
-          </div>
-        </div>
-
-        <div
-          ref={pinDiv}
-          className="right-pane"
-          style={{
-            width: '50%',
-            height: '100vh',
-            position: 'relative',
-            overflow: 'hidden',
-            display: 'flex',
-            alignItems: 'center',
-            justifyContent: 'center',
-          }}
-        >
-          <img
-            ref={img1}
-            className="i-image imagePosition"
-            src="https://static.apiseven.com/202202/apisix-dashboard.png";
-            loading="lazy"
-            alt="apisix-dashboard"
-          />
-          <img
-            ref={img2}
-            className="n-image imagePosition"
-            src="https://static.apiseven.com/202202/code-sample.png";
-            loading="lazy"
-            alt="code-snippet"
-          />
-          <img
-            ref={img3}
-            className="n-image imagePosition"
-            src="https://static.apiseven.com/202202/pluginised.png";
-            loading="lazy"
-            alt="plugin-workflow"
-          />
-        </div>
-      </div>
-      <div
-        className="feat-container-m"
-        ref={triggerDivCol}
-        style={{ width: '100%' }}
-      >
-        {/* Mobile */}
-        <div
-          ref={img1col}
-          className="hiddenDiv-col"
-          style={{ height: 'fit-content', padding: '0 0 40px 0' }}
-        >
-          <div style={{ position: 'relative', height: '100%' }}>
-            <h2 className="add-left-margin" style={{ width: 'fit-content' }}>
+    <div className="feat-container-d" ref={triggerDiv}>
+      <div className="left-pane" style={{ width: '50%', height: '100%' }}>
+        <div ref={dashboardDiv} style={{ position: 'relative' }}>
+          <div className="text-div" style={{ height: '100vh' }}>
+            <h2 className="i-text add-left-margin-feat">
               <Translate id="features.component.easyDashboard.title">
-                Easy-to-use dashboard
+                Easy-to-use Dashboard
               </Translate>
             </h2>
-            <img className="i-image-col" 
src="https://static.apiseven.com/202202/apisix-dashboard.png"; alt="Dashboard" />
-            <p className="i-text-desc-col add-left-margin">
+            <p className="i-text-desc add-left-margin-feat">
               <Translate id="features.component.easyDashboard.message">
                 The Apache APISIX Dashboard is designed to make it as easy as
                 possible for users to operate Apache APISIX through a frontend
@@ -288,54 +93,244 @@ const Features: FC = () => {
                 contribute.
               </Translate>
             </p>
-            <div
-              className="hero-ctas add-left-margin"
-              style={{ width: 'fit-content' }}
-            >
+            <div className="hero-ctas add-left-margin-feat bottom-pos">
               <Link to={useBaseUrl('downloads')} className="btn btn-download">
                 <Translate 
id="features.component.easyDashboard.downloadBtn">Downloads</Translate>
               </Link>
               <ArrowAnim />
             </div>
+            <DashboardPlayground />
           </div>
         </div>
 
-        <div
-          ref={img2col}
-          className="hiddenDiv-col"
-          style={{ height: 'fit-content', padding: '20px 0' }}
-        >
-          <h2 className="add-left-margin" style={{ width: 'fit-content' }}>
-            <Translate id="features.component.userFlexible.title">User 
Flexible</Translate>
-          </h2>
-          <img className="i-image-col" 
src="https://static.apiseven.com/202202/code-sample.png"; alt="code-sample" />
-          <p className="i-text-desc-col add-left-margin">
-            <Translate id="features.component.userFlexible.message">
-              The Apache APISIX dashboard is flexible to User demand, providing
-              option to create custom modules through code matching your
-              requirements, alongside the existing no-code toolchain.
-            </Translate>
-          </p>
+        <div ref={userfDiv} style={{ position: 'relative' }}>
+          <div className="text-div" style={{ height: '100vh' }}>
+            <h2 className="i-text add-left-margin-feat">
+              <Translate id="features.component.userFlexible.title">User 
Flexible</Translate>
+            </h2>
+            <p className="i-text-desc add-left-margin-feat">
+              <Translate id="features.component.userFlexible.message">
+                The Apache APISIX dashboard is flexible to User demand,
+                providing option to create custom modules through code matching
+                your requirements, alongside the existing no-code toolchain.
+              </Translate>
+            </p>
+          </div>
         </div>
 
-        <div
-          ref={img3col}
-          className="hiddenDiv-col"
-          style={{ height: 'fit-content', padding: '20px 0' }}
-        >
+        <div ref={pluginDiv} style={{ position: 'relative' }}>
+          <div className="text-div" style={{ height: '100vh' }}>
+            <h2 className="i-text add-left-margin-feat">
+              <Translate id="features.component.pluginised.title">Pluginised 
Workflow</Translate>
+            </h2>
+            <p className="i-text-desc add-left-margin-feat">
+              <Translate id="features.component.pluginised.message">
+                No need to reinvent the wheel again and again. Use inbuilt
+                plugins to create high performance systems in tight deadlines.
+                For something custom, there is option of building custom
+                plugins.
+              </Translate>
+            </p>
+          </div>
+        </div>
+      </div>
+
+      <div
+        ref={pinDiv}
+        className="right-pane"
+        style={{
+          width: '50%',
+          height: '100vh',
+          position: 'relative',
+          overflow: 'hidden',
+          display: 'flex',
+          alignItems: 'center',
+          justifyContent: 'center',
+        }}
+      >
+        <img
+          ref={img1}
+          className="i-image imagePosition"
+          src="https://static.apiseven.com/202202/apisix-dashboard.png";
+          loading="lazy"
+          alt="apisix-dashboard"
+        />
+        <img
+          ref={img2}
+          className="n-image imagePosition"
+          src="https://static.apiseven.com/202202/code-sample.png";
+          loading="lazy"
+          alt="code-snippet"
+        />
+        <img
+          ref={img3}
+          className="n-image imagePosition"
+          src="https://static.apiseven.com/202202/pluginised.png";
+          loading="lazy"
+          alt="plugin-workflow"
+        />
+      </div>
+    </div>
+  );
+};
+
+const FeatMobile: FC = () => {
+  const observers = [];
+  const triggerDivCol = useRef(null);
+  const img1col = useRef(null);
+  const img2col = useRef(null);
+  const img3col = useRef(null);
+
+  useEffect(() => {
+    const elems = [img1col.current, img2col.current, img3col.current];
+    for (let i = 1; i < 4; i += 1) {
+      observers.push(
+        new IntersectionObserver(
+          (entries) => {
+            entries.forEach((entry) => {
+              if (entry.isIntersecting) {
+                observers[i - 1].disconnect();
+                gsap.fromTo(
+                  elems[i - 1],
+                  {
+                    opacity: 0,
+                    y: 90,
+                  },
+                  {
+                    opacity: 1,
+                    y: 0,
+                    duration: 0.5,
+                    ease: 'power3.out',
+                    yoyo: true,
+                    yoyoEase: 'power3.inOut',
+                  },
+                );
+              }
+            });
+          },
+          {
+            root: null,
+            threshold: 0.2,
+          },
+        ),
+      );
+    }
+
+    observers.forEach((it, index) => {
+      it.observe(elems[index]);
+    });
+  }, []);
+
+  return (
+    <div
+      className="feat-container-m"
+      ref={triggerDivCol}
+      style={{ width: '100%' }}
+    >
+      <div
+        ref={img1col}
+        className="hiddenDiv-col"
+        style={{ height: 'fit-content', padding: '0 0 40px 0' }}
+      >
+        <div style={{ position: 'relative', height: '100%' }}>
           <h2 className="add-left-margin" style={{ width: 'fit-content' }}>
-            <Translate id="features.component.pluginised.title">Pluginised 
Workflow</Translate>
+            <Translate id="features.component.easyDashboard.title">
+              Easy-to-use dashboard
+            </Translate>
           </h2>
-          <img className="i-image-col" 
src="https://static.apiseven.com/202202/pluginised.png"; alt="Plugin" />
+          <img className="i-image-col" 
src="https://static.apiseven.com/202202/apisix-dashboard.png"; alt="Dashboard" />
           <p className="i-text-desc-col add-left-margin">
-            <Translate id="features.component.pluginised.message">
-              No need to reinvent the wheel again and again. Use inbuilt 
plugins
-              to create high performance systems in tight deadlines. For 
something
-              custom, there is option of building custom plugins.
+            <Translate id="features.component.easyDashboard.message">
+              The Apache APISIX Dashboard is designed to make it as easy as
+              possible for users to operate Apache APISIX through a frontend
+              interface. It’s opensource and ever evolving, feel free to
+              contribute.
             </Translate>
           </p>
+          <div
+            className="hero-ctas add-left-margin"
+            style={{ width: 'fit-content' }}
+          >
+            <Link to={useBaseUrl('downloads')} className="btn btn-download">
+              <Translate 
id="features.component.easyDashboard.downloadBtn">Downloads</Translate>
+            </Link>
+            <ArrowAnim />
+          </div>
         </div>
       </div>
+
+      <div
+        ref={img2col}
+        className="hiddenDiv-col"
+        style={{ height: 'fit-content', padding: '20px 0' }}
+      >
+        <h2 className="add-left-margin" style={{ width: 'fit-content' }}>
+          <Translate id="features.component.userFlexible.title">User 
Flexible</Translate>
+        </h2>
+        <img className="i-image-col" 
src="https://static.apiseven.com/202202/code-sample.png"; alt="code-sample" />
+        <p className="i-text-desc-col add-left-margin">
+          <Translate id="features.component.userFlexible.message">
+            The Apache APISIX dashboard is flexible to User demand, providing
+            option to create custom modules through code matching your
+            requirements, alongside the existing no-code toolchain.
+          </Translate>
+        </p>
+      </div>
+
+      <div
+        ref={img3col}
+        className="hiddenDiv-col"
+        style={{ height: 'fit-content', padding: '20px 0' }}
+      >
+        <h2 className="add-left-margin" style={{ width: 'fit-content' }}>
+          <Translate id="features.component.pluginised.title">Pluginised 
Workflow</Translate>
+        </h2>
+        <img className="i-image-col" 
src="https://static.apiseven.com/202202/pluginised.png"; alt="Plugin" />
+        <p className="i-text-desc-col add-left-margin">
+          <Translate id="features.component.pluginised.message">
+            No need to reinvent the wheel again and again. Use inbuilt plugins
+            to create high performance systems in tight deadlines. For 
something
+            custom, there is option of building custom plugins.
+          </Translate>
+        </p>
+      </div>
+    </div>
+  );
+};
+
+const FeatContainer: FC = () => {
+  gsap.registerPlugin(ScrollTrigger);
+  const windowType = useWindowType();
+
+  if (windowType === 'desktop') {
+    return <FeatDesktop />;
+  }
+
+  return <FeatMobile />;
+};
+const FeatContainerMemo = memo(FeatContainer);
+
+const Features: FC = () => {
+  const featPin = useRef(null);
+
+  return (
+    <>
+      <div ref={featPin} className="feat-top" style={{ padding: '50px 0' }}>
+        <h3 className="feat-head-desc"><Translate 
id="features.component.why.title">Why APISIX Gateway?</Translate></h3>
+        <h2 className="feat-head add-left-margin">
+          <Translate id="features.component.why.subtitle">
+            Reduce time fighting bugs, focus on designing world-class systems 
with API Gateway
+          </Translate>
+        </h2>
+        <p className="feat-desc add-left-margin">
+          <Translate id="features.component.why.message">
+            Apache APISIX is the first open-source API Gateway
+            that includes a built-in low-code Dashboard,
+            which offers a powerful and flexible UI for developers to use.
+          </Translate>
+        </p>
+      </div>
+      <FeatContainerMemo />
     </>
   );
 };
diff --git a/website/src/components/sections/HeroSection.tsx 
b/website/src/components/sections/HeroSection.tsx
index 0583c256737..9b98c405f1e 100644
--- a/website/src/components/sections/HeroSection.tsx
+++ b/website/src/components/sections/HeroSection.tsx
@@ -6,17 +6,23 @@ import Translate from '@docusaurus/Translate';
 
 import '../../css/customTheme.css';
 import BrowserOnly from '@docusaurus/BrowserOnly';
+import useWindowType from '@theme/hooks/useWindowSize';
 import ArrowAnim from '../ArrowAnim';
 
-const LazyLoadHeroCanvas = () => (
-  <BrowserOnly>
-    {() => {
-      // eslint-disable-next-line @typescript-eslint/no-var-requires, 
global-require
-      const HeroCanvas = require('../HeroCanvas').default;
-      return <HeroCanvas />;
-    }}
-  </BrowserOnly>
-);
+const LazyLoadHeroCanvas = () => {
+  const windowType = useWindowType();
+  if (windowType === 'mobile') return (null);
+
+  return (
+    <BrowserOnly>
+      {() => {
+        // eslint-disable-next-line @typescript-eslint/no-var-requires, 
global-require
+        const HeroCanvas = require('../HeroCanvas').default;
+        return <HeroCanvas />;
+      }}
+    </BrowserOnly>
+  );
+};
 
 const HeroSection: FC = () => (
   <div className="hero-sec-wrap" style={{ width: '100%' }}>
diff --git a/website/src/components/sections/OpensourcePromo.tsx 
b/website/src/components/sections/OpensourcePromo.tsx
index 373dedeb4dc..c6f63552d8d 100644
--- a/website/src/components/sections/OpensourcePromo.tsx
+++ b/website/src/components/sections/OpensourcePromo.tsx
@@ -5,10 +5,9 @@ import Link from '@docusaurus/Link';
 import useBaseUrl from '@docusaurus/useBaseUrl';
 import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
 import Translate from '@docusaurus/Translate';
-import OssCanvas from '../OssCanvas';
 
+import BrowserOnly from '@docusaurus/BrowserOnly';
 import GitHub from '../../assets/icons/github-logo.svg';
-import Video from '../Video';
 import type { VideoProps } from '../Video';
 
 const VideoChannel: FC = () => {
@@ -43,6 +42,16 @@ const videoOptions: VideoProps['options'] = {
   height: 360,
 };
 
+const LazyLoadVideo = () => (
+  <BrowserOnly>
+    {() => {
+      // eslint-disable-next-line @typescript-eslint/no-var-requires, 
global-require
+      const Video = require('../Video').default;
+      return <Video options={videoOptions} />;
+    }}
+  </BrowserOnly>
+);
+
 const OpensourcePromo: FC = () => (
   <div className="ossPromotion">
     <div className="docs-promo">
@@ -78,25 +87,26 @@ const OpensourcePromo: FC = () => (
         </div>
       </div>
       <div className="docs-promo-video">
-        <Video options={videoOptions} />
+        <LazyLoadVideo />
       </div>
     </div>
 
     <div className="oss-promo">
-      <div className="oss-promo-text">
+      <div className="oss-promo-inwrapper">
         <h3 className="oss-promo-head">
           <Translate id="openSourcePromo.component.ossPromo.title">
-            Building API Gateway together
+            Building API Gateway Together
           </Translate>
         </h3>
         <div className="oss-promo-subtitle" style={{ color: 'rgb(199, 199, 
199)' }}>
           <p>
-            <strong>
-              <Translate id="openSourcePromo.component.ossPromo.subtitle">
-                Apache APISIX is open source and ever-growing.
-                Contributors are always welcome. Reach out to us on GitHub
-              </Translate>
-            </strong>
+            <Translate id="openSourcePromo.component.ossPromo.subtitle1">
+              Apache APISIX is open source and ever-growing.
+            </Translate>
+            <br />
+            <Translate id="openSourcePromo.component.ossPromo.subtitle2">
+              Contributors are always welcome, reach out to us on GitHub.
+            </Translate>
           </p>
           <div className="oss-promo-cta">
             <GitHub style={{ width: '20px', margin: '0 10px 0 0' }} />
@@ -108,9 +118,6 @@ const OpensourcePromo: FC = () => (
           </div>
         </div>
       </div>
-      <div className="oss-promo-infograph">
-        <OssCanvas />
-      </div>
     </div>
   </div>
 );
diff --git a/website/src/css/customTheme.css b/website/src/css/customTheme.css
index bd654f96fe7..257b55283b7 100644
--- a/website/src/css/customTheme.css
+++ b/website/src/css/customTheme.css
@@ -528,8 +528,7 @@ a:hover {
 .arch-head,
 .compare-head,
 .testimonials-head,
-.docs-promo-head,
-.oss-promo-head {
+.docs-promo-head {
   font-size: 2.4rem;
   position: relative;
   left: 16.8%;
@@ -539,8 +538,7 @@ a:hover {
 .arch-subtitle,
 .compare-subtitle,
 .testimonials-subtitle,
-.docs-promo-subtitle,
-.oss-promo-subtitle {
+.docs-promo-subtitle {
   font-size: 1.13rem;
   position: relative;
   left: 16.8%;
@@ -986,29 +984,33 @@ a:hover {
 
 .oss-promo {
   display: flex;
-  padding: 50px 0;
-  flex-flow: row-reverse;
-  color: black;
-  background: #fff8f6;
+  padding: 80px 0;
+  justify-content: center;
+  background: #121010;
+  color: white;
 }
 
-.oss-promo-text {
-  width: 50%;
+.oss-promo-inwrapper {
   display: flex;
-  justify-content: center;
   flex-direction: column;
-  background: #121010;
-  color: white;
+  text-align: left;
 }
 
-.oss-promo-infograph {
-  background: #0a0a0a;
-  width: 50%;
-  height: 100%;
-  overflow: hidden;
+.oss-promo-head {
+  font-size: 2.4rem;
+}
+
+.oss-promo-subtitle {
+  font-size: 1.13rem;
+  color: #615d5d;
+  letter-spacing: 0.2px;
+  font-family: MaisonNeue-Light, sans-serif;
+  font-weight: 700;
+  text-align: center;
 }
 
 .oss-promo-cta {
+  margin: 0 auto;
   background: #080808;
   display: flex;
   width: fit-content;
@@ -1025,12 +1027,11 @@ a:hover {
 }
 
 .oss-promo-cta:hover {
-  background: #9b9b9b;
+  background: hsl(0deg 100% 100% / 80%);
   color: #080808;
-  border-style: solid;
 }
 
-.oss-promo-cta > a:hover {
+.oss-promo-cta:hover > a {
   color: #080808;
 }
 

Reply via email to