类似 Slack 评论框的动画 React Native。

huangapple go评论59阅读模式
英文:

Animation like slack comment box react-native

问题

我正在开发一个评论框,我想在向上滑动操作时将其扩展到设备的高度,并在向下滑动操作时将其恢复到原始高度。但是,我无法为其添加动画,因为函数的工作方式不符合我的期望。可以参考Slack评论框动画来了解具体效果。

以下是我的代码:

代码

Snack链接:https://snack.expo.dev/@sidd0328/animate-comment-box

const deviceHeight = Dimensions.get("window").height;
const height = useRef(new Animated.Value(1)).current;
const videoPlaceholderSize = 80;
const parentContainerExpandedHeight = deviceHeight * 0.85 - keyboardHeight;
const childContainerExpandedHeight = deviceHeight * 0.82 - keyboardHeight;
const textContainerExpandedHeight = deviceHeight * 0.75 - keyboardHeight;

useEffect(() => {
  Animated.timing(height, {
    toValue: isExpanded ? parentContainerExpandedHeight : 0,
    duration: 400,
    useNativeDriver: false,
  }).start();
}, [isExpanded]);

return (
  <GestureRecognizer
    onSwipeUp={() => setIsExpanded(true)}
    onSwipeDown={() => setIsExpanded(false)}
  >
    <Animated.View
      style={[
        getStyle.parentContainer,
        isExpanded && {
          height: height,
        },
      ]}
    >
      <View style={getStyle.handlebar} />
      <View
        style={[
          getStyle.childContainer,
          isExpanded && { height: childContainerExpandedHeight },
        ]}
      >
        <View
          style={[
            getStyle.textImageWrapper,
            isExpanded && { height: textContainerExpandedHeight },
          ]}
        >
          <ScrollView showsVerticalScrollIndicator={false}>
            <>
              <TextInput
                ref={inputRef}
                value={inputValue}
                style={getStyle.input}
                placeholder={placeholder || i18n.t("community.writeComment")}
                placeholderTextColor="gray"
                multiline
                textAlignVertical="top"
                onChangeText={handleChangeText}
                maxLength={maxLength || 500}
              />
            </>
          </ScrollView>
          {Object.keys(selectedImageFile).length ? (
            isVideoByType(selectedImageFile.type) && selectedImage.length > 0 ? (
              <View style={getStyle.videoPlaceholder}>
                <VideoPlaceholder
                  variant="nonPressable"
                  height={videoPlaceholderSize}
                  width={videoPlaceholderSize}
                />
                <TouchableOpacity style={getStyle.backImageWrapper} onPress={removeImage}>
                  <VectorImage
                    source={Icons.removeCircle2}
                    width={IconSize.m}
                    height={IconSize.m}
                  />
                </TouchableOpacity>
              </View>
            ) : selectedImage.length > 0 ? (
              <View style={getStyle.imagePlaceholder}>
                <ImagesLayout
                  path="CommentBox"
                  images={selectedImage}
                  isRemoveImageIconVisible={true}
                  onRemoveImagePress={removeImage}
                />
              </View>
            ) : null
          ) : null}
        </View>
        {!isEditing ? (
          <View style={getStyle.container}>
            <TouchableOpacity
              style={[getStyle.submitButton, getStyle.addImageButtonWrapper]}
              onPress={() => setImageSelectionVisible(true)}
            >
              <VectorImage source={Icons.pictureSingle2} width={IconSize.m} height={IconSize.m} />
            </TouchableOpacity>
            <TouchableOpacity
              onPress={inputValue.length ? onPressSubmit : () => {}}
              style={[
                getStyle.submitButton,
                {
                  backgroundColor: inputValue.length ? buttonColor.newPrimaryColor : colors.gray3,
                },
              ]}
              activeOpacity={inputValue.length ? 0.2 : 1}
            >
              <VectorImage
                source={Icons.send2}
                style={getStyle.imageColor}
                width={IconSize.m}
                height={IconSize.m}
              />
            </TouchableOpacity>
            <ImageSelectionMethod
              isVisible={isImageSelectionVisible}
              isVideo={isVideoCaptureEnabled}
              onClose={() => setImageSelectionVisible(false)}
              onCameraPicker={showCamera}
              onImagePicker={
                isVideoCaptureEnabled ? showPhotoAndVideoGalleryPicker : showPhotoGalleryPicker
              }
              onVideoPicker={showVideoRecorder}
            />
          </View>
        ) : (
          <View style={getStyle.buttonContainer}>
            <View style={getStyle.buttonWrapper}>
              <Button
                variant={Variant.SECONDARY_OUTLINED_SMALL}
                label={i18n.t("community.cancelText")}
                onPress={cancelEditing}
              />
            </View>
            <Button
              variant={Variant.PRIMARY_SMALL}
              label={i18n.t("contactForms.createContactFormScreen.saveButtonLabel")}
              isEnabled={Boolean(inputValue.length)}
              onPress={onPressSubmit}
            />
          </View>
        )}
      </View>
      <View style={getStyle.bottomHandlebarContainer} />
    </Animated.View>
  </GestureRecognizer>
);

上述实现的动画视频:https://www.dropbox.com/s/ivfaqd397f3jutz/Screen%20Recording%202023-06-28%20at%205.53.38%20PM.mov?dl=0

我想要的效果

https://www.dropbox.com/s/ylktmw3j1z0ec5d/IMG_5044.mov?dl=0

非常感谢任何建议或帮助!

英文:

I am working on a comment box which I am expanding to the height of device on swipe up action and back to its original height on swipe down action. But I am not able to add the animation to it as the function does not work the way i wanted it to be. For the reference we can talk about the slack comment box animation.

Here is what my code looks like :

code :

Snack link : https://snack.expo.dev/@sidd0328/animate-comment-box

const deviceHeight = Dimensions.get(&quot;window&quot;).height;
const height = useRef(new Animated.Value(1)).current;
const videoPlaceholderSize = 80;
const parentContainerExpandedHeight = deviceHeight * 0.85 - keyboardHeight;
const childContainerExpandedHeight = deviceHeight * 0.82 - keyboardHeight;
const textContainerExpandedHeight = deviceHeight * 0.75 - keyboardHeight;
useEffect(() =&gt; {
Animated.timing(height, {
toValue: isExpanded ? parentContainerExpandedHeight : 0,
duration: 400,
useNativeDriver: false,
}).start();
}, [isExpanded]);
return (
&lt;GestureRecognizer
onSwipeUp={() =&gt; setIsExpanded(true)}
onSwipeDown={() =&gt; setIsExpanded(false)}
&gt;
&lt;Animated.View
style={[
getStyle.parentContainer,
isExpanded &amp;&amp; {
height: height,
},
]}
&gt;
&lt;View style={getStyle.handlebar} /&gt;
&lt;View
style={[getStyle.childContainer, isExpanded &amp;&amp; { height: childContainerExpandedHeight }]}
&gt;
&lt;View
style={[
getStyle.textImageWrapper,
isExpanded &amp;&amp; { height: textContainerExpandedHeight },
]}
&gt;
&lt;ScrollView showsVerticalScrollIndicator={false}&gt;
&lt;&gt;
&lt;TextInput
ref={inputRef}
value={inputValue}
style={getStyle.input}
placeholder={placeholder || i18n.t(&quot;community.writeComment&quot;)}
placeholderTextColor=&quot;gray&quot;
multiline
textAlignVertical=&quot;top&quot;
onChangeText={handleChangeText}
maxLength={maxLength || 500}
/&gt;
&lt;/&gt;
&lt;/ScrollView&gt;
{Object.keys(selectedImageFile).length ? (
isVideoByType(selectedImageFile.type) &amp;&amp; selectedImage.length &gt; 0 ? (
&lt;View style={getStyle.videoPlaceholder}&gt;
&lt;VideoPlaceholder
variant=&quot;nonPressable&quot;
height={videoPlaceholderSize}
width={videoPlaceholderSize}
/&gt;
&lt;TouchableOpacity style={getStyle.backImageWrapper} onPress={removeImage}&gt;
&lt;VectorImage
source={Icons.removeCircle2}
width={IconSize.m}
height={IconSize.m}
/&gt;
&lt;/TouchableOpacity&gt;
&lt;/View&gt;
) : selectedImage.length &gt; 0 ? (
&lt;View style={getStyle.imagePlaceholder}&gt;
&lt;ImagesLayout
path=&quot;CommentBox&quot;
images={selectedImage}
isRemoveImageIconVisible={true}
onRemoveImagePress={removeImage}
/&gt;
&lt;/View&gt;
) : null
) : null}
&lt;/View&gt;
{!isEditing ? (
&lt;View style={getStyle.container}&gt;
&lt;TouchableOpacity
style={[getStyle.submitButton, getStyle.addImageButtonWrapper]}
onPress={() =&gt; setImageSelectionVisible(true)}
&gt;
&lt;VectorImage source={Icons.pictureSingle2} width={IconSize.m} height={IconSize.m} /&gt;
&lt;/TouchableOpacity&gt;
&lt;TouchableOpacity
onPress={inputValue.length ? onPressSubmit : () =&gt; {}}
style={[
getStyle.submitButton,
{
backgroundColor: inputValue.length ? buttonColor.newPrimaryColor : colors.gray3,
},
]}
activeOpacity={inputValue.length ? 0.2 : 1}
&gt;
&lt;VectorImage
source={Icons.send2}
style={getStyle.imageColor}
width={IconSize.m}
height={IconSize.m}
/&gt;
&lt;/TouchableOpacity&gt;
&lt;ImageSelectionMethod
isVisible={isImageSelectionVisible}
isVideo={isVideoCaptureEnabled}
onClose={() =&gt; setImageSelectionVisible(false)}
onCameraPicker={showCamera}
onImagePicker={
isVideoCaptureEnabled ? showPhotoAndVideoGalleryPicker : showPhotoGalleryPicker
}
onVideoPicker={showVideoRecorder}
/&gt;
&lt;/View&gt;
) : (
&lt;View style={getStyle.buttonContainer}&gt;
&lt;View style={getStyle.buttonWrapper}&gt;
&lt;Button
variant={Variant.SECONDARY_OUTLINED_SMALL}
label={i18n.t(&quot;community.cancelText&quot;)}
onPress={cancelEditing}
/&gt;
&lt;/View&gt;
&lt;Button
variant={Variant.PRIMARY_SMALL}
label={i18n.t(&quot;contactForms.createContactFormScreen.saveButtonLabel&quot;)}
isEnabled={Boolean(inputValue.length)}
onPress={onPressSubmit}
/&gt;
&lt;/View&gt;
)}
&lt;/View&gt;
&lt;View style={getStyle.bottomHandlebarContainer} /&gt;
&lt;/Animated.View&gt;
&lt;/GestureRecognizer&gt;
);

animation video for above implementation : https://www.dropbox.com/s/ivfaqd397f3jutz/Screen%20Recording%202023-06-28%20at%205.53.38%20PM.mov?dl=0

What I want :

https://www.dropbox.com/s/ylktmw3j1z0ec5d/IMG_5044.mov?dl=0

Any suggestions or help would be greatly appreciated!!!

答案1

得分: 1

我检查了你分享的小吃链接,并在其中添加了动画,这里是具有期望行为的小吃。

解释

我已经更新了与TextInput及其容器相关的一些样式,并删除了你添加的用于在样式中添加动画高度的检查。

          styles.parentContainer,
isExpanded &amp;&amp; {       // 这部分已删除
height: height,
},
]}

我还删除了增加/减少容器高度的useEffect,而是添加了两个单独的函数来执行相同的操作,并在swipeUp/swipeDown回调中调用这些函数。

此外,为了获得正确的增加高度,从parentContainerExpandedHeight中减去96。这里的96是TextInput容器的总最小高度(总最小高度=垂直填充/边距+高度)。

  const parentContainerExpandedHeight = deviceHeight - 96;

代码

滑动手势回调函数

  const onSwipeUp = () =&gt; {
    console.log(&#39;向上滑动&#39;);
    isExpanded.current = true;
    Animated.timing(height, {
      toValue: parentContainerExpandedHeight,
      duration: 400,
      useNativeDriver: false,
    }).start();
  };

  const onSwipeDown = () =&gt; {
    console.log(&#39;向下滑动&#39;);
    isExpanded.current = false;
    Animated.timing(height, {
      toValue: 0,
      duration: 400,
      useNativeDriver: false,
    }).start();
  };

带有TextInput的手势处理器

      &lt;GestureRecognizer
        onSwipeUp={state =&gt; onSwipeUp()}
        onSwipeDown={state =&gt; onSwipeDown()}&gt;
        &lt;View style={[styles.parentContainer]}&gt;
          &lt;View style={styles.handlebar} /&gt;
          &lt;Animated.View style={[styles.childContainer, {minHeight: height}]}&gt;
            &lt;View style={[styles.anotherChildContainer]}&gt;
              &lt;View style={[styles.textImageWrapper]}&gt;
                &lt;ScrollView showsVerticalScrollIndicator={false}&gt;
                  &lt;&gt;
                    &lt;TextInput
                      value={text}
                      onChangeText={onChangeText}
                      placeholder={&#39;占位符&#39;}
                      placeholderTextColor=&quot;灰色&quot;
                      multiline
                      textAlignVertical=&quot;top&quot;
                      style={{
                        textAlignVertical: &#39;top&#39;,
                        marginBottom: 10,
                      }}
                      maxLength={500}
                    /&gt;
                  &lt;/&gt;
                &lt;/ScrollView&gt;
              &lt;/View&gt;
              &lt;View style={styles.container}&gt;
                &lt;TouchableOpacity
                  style={styles.submitButton}&gt;&lt;/TouchableOpacity&gt;
                &lt;TouchableOpacity
                  onPress={() =&gt; {}}
                  style={styles.submitButton}&gt;&lt;/TouchableOpacity&gt;
              &lt;/View&gt;
            &lt;/View&gt;
          &lt;/Animated.View&gt;
        &lt;/View&gt;
      &lt;/GestureRecognizer&gt;

输出

类似 Slack 评论框的动画 React Native。

英文:

I checked the snack link shared by you and added the animation in it, here is the snack with expected behavior.

Explanation

I have updated some of the styling related to TextInput and its containers and removed the check you added for adding the animated height in style.

          styles.parentContainer,
isExpanded &amp;&amp; {       // removed this
height: height,
},
]}

I have also removed the useEffect which was increasing/descresing the height of container,instead I added 2 separate functions to do the same thing and called this functions in swipeUp/swipeDown callbacks.

Also, To get the correct increased height, substract 96 from parentContainerExpandedHeight. Here 96 is the total minimum height of the container of TextInput (total minimum height = vertical padding/margin + height).

  const parentContainerExpandedHeight = deviceHeight - 96;

Code

Swipe gesture callback function

  const onSwipeUp = () =&gt; {
console.log(&#39;swipe up&#39;);
isExpanded.current = true;
Animated.timing(height, {
toValue: parentContainerExpandedHeight,
duration: 400,
useNativeDriver: false,
}).start();
};
const onSwipeDown = () =&gt; {
console.log(&#39;swipe down&#39;);
isExpanded.current = false;
Animated.timing(height, {
toValue: 0,
duration: 400,
useNativeDriver: false,
}).start();
};

Gesture handler with TextInput

      &lt;GestureRecognizer
onSwipeUp={state =&gt; onSwipeUp()}
onSwipeDown={state =&gt; onSwipeDown()}&gt;
&lt;View style={[styles.parentContainer]}&gt;
&lt;View style={styles.handlebar} /&gt;
&lt;Animated.View style={[styles.childContainer, {minHeight: height}]}&gt;
&lt;View style={[styles.anotherChildContainer]}&gt;
&lt;View style={[styles.textImageWrapper]}&gt;
&lt;ScrollView showsVerticalScrollIndicator={false}&gt;
&lt;&gt;
&lt;TextInput
value={text}
onChangeText={onChangeText}
placeholder={&#39;placeholder&#39;}
placeholderTextColor=&quot;gray&quot;
multiline
textAlignVertical=&quot;top&quot;
style={{
textAlignVertical: &#39;top&#39;,
marginBottom: 10,
}}
maxLength={500}
/&gt;
&lt;/&gt;
&lt;/ScrollView&gt;
&lt;/View&gt;
&lt;View style={styles.container}&gt;
&lt;TouchableOpacity
style={styles.submitButton}&gt;&lt;/TouchableOpacity&gt;
&lt;TouchableOpacity
onPress={() =&gt; {}}
style={styles.submitButton}&gt;&lt;/TouchableOpacity&gt;
&lt;/View&gt;
&lt;/View&gt;
&lt;/Animated.View&gt;
&lt;/View&gt;
&lt;/GestureRecognizer&gt;

Output

类似 Slack 评论框的动画 React Native。

huangapple
  • 本文由 发表于 2023年6月26日 20:18:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76556614.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定