1

我一直在尝试重新创建TypeForm用于创建问题的类似 UI。

如果您在他们的编辑器中注意到,当您使用箭头时,右侧会像一页一样上下滚动。 在此处输入图像描述

对于我的应用程序,我希望它以相同的方式滚动,但通过单击左侧的问题来代替

它几乎可以工作,但是当第一次以相反的顺序单击问题时,动画中有一个小错误 - 它重叠 - 这就是问题所在。我正在努力使上下动画的问题永远不会相互重叠,你能帮忙吗?

目前它的工作方式是根据您是否选择“上一个”问题来更改动画的initial,animate和值exit

在此处输入图像描述

这是一个代码框的链接,因为我无法让下面的代码片段正确运行......

import React, { useState, useEffect, useRef } from "react";

import { motion, AnimatePresence } from "framer-motion"
import uuid from 'uuid/v4';
import './styles.css';

const App = () => {
  const [display, setDisplay] = useState(false);
  const [questions, setQuestions] = useState([{id:uuid(), q: '', active:true}]);
  const [isPrevious, setIsPrevious] = useState(false);
  const [prev, setPrev] = useState(0);
  const toggleMenu = () => setDisplay(!display);
  const selectType = ()=> {
    setDisplay(false);
    setPrev(questions.findIndex(x=>x.active === true))
    setQuestions(prevState => {
      let updated = prevState.map(question=>{
        question.active = false;
        return question
      })
      updated.push({id:uuid(), q: '', active:true})
      return updated
    })
  }
  useEffect(()=>{
    setPrev(questions.findIndex(x=>x.active === true))
  },[questions])
  const updateQuestions = ({id, q}) => setQuestions(prevState => prevState.map((question, index) => {
    question.active = false;
    if(question.id === id) {
      console.log({'prev': prev, 'current': index, 'isPrev?': isPrevious})
      if(prev > index) {
        setIsPrevious(true)
      } else {
        setIsPrevious(false)
      }
      question.q = q
      question.active = true
    } 
    return question
  }))
  const removeQuestion = (id) => setQuestions(prevState => prevState.filter(question => question.id !== id))
  return (
    <> 
      <Navbar />
      <main className="flex items-stretch justify-between">
        <div className="w-1/2">
          {questions.map(question=>
            <Qshort key={question.id} removeQuestion={removeQuestion} data={question} updateQuestion={updateQuestions}/>
          )}
          <div className="relative mt-10 px-4">
            <button onClick={toggleMenu} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Add your first question</button>
            <div className={(display ? "flex " : "hidden ") + "absolute bg-white p-3 flex-col shadow-md w-1/2 rounded-lg"}>
              <button onClick={() => selectType("1")} className="my-3 mt-0 p-3 hover:bg-gray-200 rounded">Short question</button>             
            </div>
          </div>
        </div>
        <div className="rightSide relative w-1/2 bg-gray-200 flex flex-col items-center justify-center">
          <AnimatePresence finishBeforeExit>
          {questions.length &&
              <motion.div
              transition={{ duration: 1, type: "tween" }}
                key={questions[questions.findIndex(x=>x.active)].id}
                
                initial={{opacity:0, bottom:isPrevious ? '100%' : '-50%'}} 
                animate={{opacity:1, bottom:'50%'}} 
                exit={{opacity:0, bottom:isPrevious ? '-20%' : '100%'}} 
                className="absolute flex flex-col w-64 w-2/3"
              >
              <p className="text-2xl pl-3 text-gray-600">{questions[questions.findIndex(x=>x.active)].q}</p>
              <input 
                placeholder="Type your answer here...." 
                type="text" 
                className="mt-3 w-full p-3 bg-transparent border-b-4 text-2xl"
              />
            </motion.div>
          }
          </AnimatePresence>
          {questions.length && 
            <button className="absolute mb-3 mr-3 bottom-0 right-0 bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-6 rounded">Next step</button>
          }
        </div>
      </main>
    </>
  );
};


const Qshort = ({updateQuestion,removeQuestion,data}) => {
  return (
    <div className={(data.active ? 'border-green-500 ':'border-transparent')+" border relative bg-gray-300 py-8 pb-12 pl-4 mb-2"}>
      <input 
        onFocus={(e)=>updateQuestion({id:data.id, q:e.target.value})} 
        onChange={(e)=>updateQuestion({id:data.id, q:e.target.value})} 
        type="text" 
        className="w-full bg-transparent" 
        placeholder="Type your question here..."
      />
      <button onClick={()=>removeQuestion(data.id)} className="absolute bottom-0 right-0 mr-3 mb-3 text-xs text-blue-500 hover:text-blue-700">Remove</button>
    </div> 
  );
};

const Navbar = () => {
  return (
    <nav className="relative flex items-center justify-between flex-wrap bg-blue-500 p-6">
      <div className="flex items-center flex-shrink-0 text-white mr-6">
        <span className="font-semibold text-xl tracking-tight">Aquire</span>
      </div>
      <div className="block lg:hidden">
        <button className="flex items-center px-3 py-2 border rounded text-teal-200 border-teal-400 hover:text-white hover:border-white">
          <svg
            className="fill-current h-3 w-3"
            viewBox="0 0 20 20"
            xmlns="http://www.w3.org/2000/svg"
          >
            <title>Menu</title>
            <path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" />
          </svg>
        </button>
      </div>
    </nav>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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>
<script src="https://cdn.jsdelivr.net/npm/uuid.v4@1.0.0/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/framer-motion@0.6.6/dist/framer-motion.js"></script>

  <div id="root"></div>

4

1 回答 1

1

我以前从未使用过成帧器运动。这很有趣,有点酷。但是我认为您一旦设置就无法更新元素退出属性。这个问题有可能在以后的版本中得到解决。

我将您的问题重构为 react-spring。我更熟悉,我认为它更成熟。它正在使用这个库。我不知道在这个项目中更改库是否适合您。

您可以这样定义转换:

const transitions = useTransition(
  questions.filter(x => x.active),
  item => item.id,
  {
    from: { opacity: 0, bottom: isPrevious.current ? "100%" : "-50%" },
    enter: { opacity: 1, bottom: "50%" },
    leave: { opacity: 0, bottom: isPrevious.current ? "-20%" : "100%" }
  }
);

这就是你使用它的方式。

{questions.length &&
  transitions.map(({ item, props, key }) => (
    <animated.div
      className="absolute flex flex-col w-64 w-2/3"
      key={key}
      style={props}
    >
      <p className="text-2xl pl-3 text-gray-600">{item.q}</p>
      <input
        placeholder="Type your answer here...."
        type="text"
        className="mt-3 w-full p-3 bg-transparent border-b-4 text-2xl"
      />
    </animated.div>
  ))
}

我必须将 isPrevious 更改为 ref 以与其他状态同步。

const isPrevious = useRef(false);

https://codesandbox.io/s/condescending-montalcini-dywpu

于 2020-02-04T15:53:01.337 回答