You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为新闻卡片设置移动端上滑查看详情后自动回弹的滑动效果

移动端上滑查看卡片详情实现方案

实现思路

  • 基于触摸事件(touchstart/touchmove/touchend)监听用户滑动动作
  • 实时控制信息栏的偏移量,实现滑动时信息栏跟随手指移动的效果
  • 滑动结束后自动触发回弹复位,同时区分滑动和点击动作,避免滑动时误触发卡片跳转
  • 兼容原有桌面端hover效果,不影响原有桌面端交互

代码修改

1. NewsCard.js 改动

import PropTypes from "prop-types";
import { useState, useRef } from "react";
import Image from "next/image";
import Link from "next/link";
import styles from "./NewsCard.module.scss";
import DeleteIcon from "../../public/svg/basketIcon.svg";

export default function NewsCard({
  newsData,
  color,
  canDelete,
  onDeleteIconClick,
}) {
  // 触摸起始Y坐标
  const startY = useRef(null);
  // 信息栏当前偏移量
  const [translateY, setTranslateY] = useState(0);
  // 是否为滑动操作(用于区分点击,避免误跳转)
  const [isSwiping, setIsSwiping] = useState(false);
  // 信息栏最大上滑距离,和原有hover偏移量保持一致
  const MAX_OFFSET = 208;
  // 滑动判定阈值,移动超过5px才算滑动操作
  const SWIPE_THRESHOLD = 5;

  const handleTouchStart = (e) => {
    startY.current = e.touches[0].clientY;
    setIsSwiping(false);
  };

  const handleTouchMove = (e) => {
    if (startY.current === null) return;
    const currentY = e.touches[0].clientY;
    const deltaY = startY.current - currentY;
    // 仅处理上滑动作
    if (deltaY > 0) {
      if (deltaY > SWIPE_THRESHOLD) {
        setIsSwiping(true);
      }
      // 限制最大上滑距离
      const offset = Math.min(deltaY, MAX_OFFSET);
      setTranslateY(-offset);
    }
  };

  const handleTouchEnd = () => {
    startY.current = null;
    // 松手后自动回弹复位
    setTranslateY(0);
    // 延迟重置滑动状态,避免点击事件先触发
    setTimeout(() => setIsSwiping(false), 300);
  };

  const handleLinkClick = (e) => {
    // 滑动状态下阻止跳转
    if (isSwiping) {
      e.preventDefault();
    }
  };

  return (
    <Link href={newsData.url} passHref>
      <div className={styles.card} onClick={handleLinkClick}>
        {canDelete ? (
          <div className={styles.deleteIcon}>
            <DeleteIcon
              onClick={(e) => {
                e.stopPropagation();
                onDeleteIconClick(newsData);
              }}
            />
          </div>
        ) : null}
        <Image
          src={newsData.imgUrl}
          alt="news-card"
          layout="fill"
          className={styles.image}
        />
        <div
          className={`${styles.info} ${translateY < 0 ? styles.active : ''}`}
          style={{ 
            background: color,
            transform: `translateY(${translateY}px)`
          }}
          onTouchStart={handleTouchStart}
          onTouchMove={handleTouchMove}
          onTouchEnd={handleTouchEnd}
        >
          {newsData.text}
        </div>
      </div>
    </Link>
  );
}

NewsCard.propTypes = {
  newsData: PropTypes.object,
  color: PropTypes.string,
  canDelete: PropTypes.bool,
  onDeleteIconClick: PropTypes.func,
};

2. SCSS 改动

@import "../../styles/shared.scss";

.card {
  width: 350px;
  height: 344px;
  margin-right: 20px;
  position: relative;
  border-radius: 3px;
  cursor: pointer;
  overflow: hidden;
  touch-action: pan-y; // 优化移动端触摸体验,避免浏览器默认行为干扰
  @media screen and (max-width: 1150px) {
        min-width: 250px;
        min-height: 333px;
      }
}

.info {
  height: 256px;
  top: 308px;
  width: 100%;
  position: absolute;
  bottom: 0;
  border-radius: 0px 0px 3px 3px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  font-weight: 600;
  font-size: 12px;
  line-height: 22px;
  letter-spacing: 0.2px;
  color: $color-primary-light;
  padding: 10px 7px 7px 6px;
  transition: transform 0.5s ease; // 单独指定transform的过渡,避免滑动时卡顿
}

// 桌面端hover效果和移动端active效果共用样式
.info:hover, .info.active {
  transform: translateY(-208px);
  -webkit-line-clamp: 100;
  padding-top: 30px;
}

// 移动端禁用hover效果,避免点击后hover状态残留
@media screen and (max-width: 1150px) {
  .info:hover {
    transform: none;
    -webkit-line-clamp: 1;
    padding-top: 10px;
  }
}

.image {
  border-radius: 3px;
}
.deleteIcon {
  border: none;
  position: absolute;
  cursor: pointer;
  top: 6px;
  right: 6px;
  z-index: 3;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0;
  svg {
    width: 15px;
    height: 15px;
  }
  border-radius: 50%;
  width: 25px;
  height: 25px;
  background: $color-primary-light;
}

功能说明

  • 桌面端保留原有的鼠标悬浮查看详情的交互逻辑
  • 移动端用户手指上滑信息栏时,信息栏会跟随手指移动,松开手指后自动回弹到初始位置
  • 滑动过程中不会触发卡片跳转,只有正常点击才会跳转至对应的Instagram帖子
  • 滑动最大距离限制为208px,和原有桌面端悬浮展开的高度保持一致,交互体验统一

内容的提问来源于stack exchange,提问作者Andrew_25

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 00:45:05