【问题标题】:SVG scale animation slow on iOSiOS上的SVG缩放动画很慢
【发布时间】:2020-08-27 11:13:46
【问题描述】:

我正在尝试向 SVG 添加缩放动画。它在基于 Webkit 的浏览器中运行良好,但在 iOS Chrome 和 Safari 上,动画非常慢。 Here 是我要制作动画的页面。这是相关代码...

const HomeHeading = styled.svg`
  margin: 0;
  color: rgba(255, 255, 255, 1);
  height: 100vh;
  width: 100vw;
  position: fixed;
  top: 0;
  left: 0;
  backface-visibility: hidden;
  perspective: 1000;
  transform: scale(
      ${props =>
        props.scrollPosition / props.scale < 1
          ? 1
          : props.scrollPosition / props.scale}
    )
    translateZ(0);
  transform-origin: 42% 56%;

  @media screen and (max-width: 480px) {
    transform-origin: 43% 38% !important;
  }

  rect {
    -webkit-mask: url(#mask);
    mask: url(#mask);
    fill: #f00;
  }

  defs {
    mask {
      rect {
        fill: white;
      }
      text {
        transform: translateY(10%);
        font-size: 8vw;

        @media screen and (max-width: 480px) {
          transform: translateY(0);
        }

        &:last-child {
          @media screen and (max-width: 480px) {
            transform: translateY(0);
          }
          transform: translateY(20%);
        }
      }
    }
  }
`;
const HomeSubHomeSectionHeading = styled.section`
  width: 100vw;
  height: 100vh;
  top: 0;
  left: 0;
  background: #1ecbe1;
  position: fixed !important;
  display: flex;
  align-content: center;
  justify-content: center;
  flex-wrap: wrap;
`;

const ColorChanger = styled.div`
  position: fixed;
  width: 100vw;
  height: 100vh;
  top: 0;
  left: 0;
  z-index: 3;
  background-color: rgb(255, 255, 255);
  opacity: ${props => 3000 / props.scrollPosition / 20};
`;

const ImageStamp = styled.div`
  width: 280px;
  height: auto;
  display: inline-block;
  padding: 10px;
  background: white;
  position: relative;
  -webkit-filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.5));
  background: radial-gradient(
    transparent 0px,
    transparent 4px,
    white 4px,
    white
  );
  background-size: 20px 20px;
  background-position: -10px -10px;

  &:after {
    content: "";
    position: absolute;
    left: 5px;
    top: 5px;
    right: 5px;
    bottom: 5px;
    z-index: -1;
  }

  @media screen and (max-width: 768px) {
    width: 200px;
    margin-bottom: 30px;
  }
`;

const MeSection = styled.section`
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
`;

const MePhoto = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 0 33.333%;
`;

const MeBio = styled.div`
  flex: 1 0 66.666%;
  color: #ffffff;
  padding: 0 30px;
`;

const MeSocials = styled.div`
  svg {
    margin: 0 10px;
  }
`;

const SocialLink = styled.a`
  color: #fff;
  &:hover {
    color: #f1f1f1;
  }
`;

const IndexPage = props => {
  const [scrollPosition, setScrollPosition] = useState(0);
  const [scale, setScale] = useState(0);

  useEffect(() => {
    window.addEventListener("scroll", handleScroll, { passive: true });
  });

  const handleScroll = () => {
    const position = window.scrollY;
    setScale(document && document.width > 500 ? 20 : 5);
    setScrollPosition(position);
  };

  return (
    <Layout>
      <HomeSubHomeSectionHeading>
        <ColorChanger scrollPosition={scrollPosition}></ColorChanger>
        <MeSection>
          <div className="container">
            <MePhoto>
              <ImageStamp>
                <Img fluid={props.data.mattImage.childImageSharp.fluid} />
              </ImageStamp>
            </MePhoto>
            <MeBio>
              <h3>Hi, I'm Matt!</h3>
              <hr />
              <p>
                I'm a Lead Frontend Developer currently based at Oliver Wyman
                Digital. I have experience in a range of frontend technologies
                and practices; more recently dabbling with AB testing, VueCLI
                and Typescript.
              </p>
              <p>
                Outside of the web world, I like to run, travel and like to
                watch movies. Apart from Toy Story 1, I cried when I found out
                Buzz Lightyear couldn't fly.
              </p>
              <h4>Find out more</h4>
              <hr />
              <MeSocials>
                <SocialLink
                  href="https://uk.linkedin.com/in/mattmaclennan"
                  target="_blank"
                >
                  <FontAwesomeIcon icon={faLinkedin} />
                </SocialLink>
                <SocialLink href="https://github.com/mmaclenn" target="_blank">
                  <FontAwesomeIcon icon={faGithub} />
                </SocialLink>
              </MeSocials>
            </MeBio>
          </div>
        </MeSection>
      </HomeSubHomeSectionHeading>
      <HomeHeading
        preserveAspectRatio="xMinYMin meet"
        scale={scale}
        scrollPosition={scrollPosition}
      >
        <defs>
          <mask id="mask" x="0" y="0" width="100%" height="100%">
            <rect x="0" y="0" width="100%" height="100%" fill="#fff"></rect>
            <text x="50%" y="40%" textAnchor="middle">
              Matt Maclennan
            </text>
            <text id="editText" x="50%" y="45%" textAnchor="middle">
              Web Developer
            </text>
          </mask>
        </defs>
        <rect
          x="0"
          y="0"
          width="100%"
          height="100%"
          fill="#E1341E"
          id="mask"
        ></rect>
      </HomeHeading>
    </Layout>
  );
};

export const fluidImage = graphql`
  fragment fluidImage on File {
    childImageSharp {
      fluid(maxWidth: 1000) {
        ...GatsbyImageSharpFluid
      }
    }
  }
`;

export const pageQuery = graphql`
  query {
    mattImage: file(relativePath: { eq: "me.jpg" }) {
      ...fluidImage
    }
  }
`;

export default IndexPage;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

我知道我只能为变换和不透明度设置动画,我还添加了translateZ 以使用硬件渲染,并将backface-visibility 添加到 CSS 中,但没有运气。

任何想法我哪里出错了?

编辑:根据 cmets,我已尝试使用此 package 来限制滚动回调。这是我正在使用的基于此包的代码...

useScrollPosition(
    ({ prevPos, currPos }) => {
      setScale(document && document.width > 500 ? 20 : 5);

      const shouldBeStyle = {
        transform: `scale(${
          Math.abs(currPos.y) < 9 ? 1 : Math.abs(currPos.y) / scale
        }) translateZ(0)`,
        pointerEvents: `${Math.abs(currPos.y) > 1000 ? "none" : "auto"}`,
      };

      const opacityStyle = {
        opacity: 1000 / Math.abs(currPos.y) / 20,
      };

      if (JSON.stringify(shouldBeStyle) === JSON.stringify(scrollStyling))
        return;

      setOpactiyStyling(opacityStyle);

      setScrollStyling(shouldBeStyle);
    },
    [scrollStyling, opacityStyling]
  );

【问题讨论】:

    标签: javascript css reactjs svg styled-components


    【解决方案1】:
      useEffect(() => {
       window.addEventListener("scroll", handleScroll, { passive: true });
      });
    

    这会在每次道具更改时添加一个事件侦听器。您可能只想这样做一次,例如:

      useEffect(() => {
       window.addEventListener("scroll", handleScroll, { passive: true });
      }, []);
    

    或者至少将一些依赖项放在这些括号中。见:https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

    【讨论】:

    • 嘿,好主意,我试了一下,但不幸的是在 iOS 上没有任何区别
    • 接下来我会尝试将你的一些计算包装在记忆回调中。每次滚动位置更改时,您都会重新计算 svg 大小。在 Firefox 中,当我向下和向上滚动几次时,我的 CPU 风扇启动,页面甚至在一分钟后崩溃。您可以提前计算值以真正加快速度,但至少您不应该重新计算您以前见过的值。
    • 更新:尝试使用回调和限制滚动,但这也没有帮助。我同意这个建议作为一个普遍的改进!我觉得这与 CSS 有关
    【解决方案2】:

    您可以使用它,也需要在组件卸载后返回清理。希望对你有帮助

    useEffect(() => {
      window.addEventListener("scroll", handleScroll, { passive: true });
    
      return () => {
        window.removeEventListener("scroll", handleScroll, { passive: true });
      };
    }, []);
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-24
      • 2014-05-03
      相关资源
      最近更新 更多