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

ReactJS博客基于字符数的PDF式分页实现技术问询

实现基于字符数的React分页视图

问题描述

我正在用ReactJS开发博客网站,想让用户点击查看全文时,替代常规的滚动浏览,实现类似PDF阅读器的分页视图——用户点击按钮就能切换页面继续阅读。请问怎么实现不按单词数、只按字符数限制每页内容?比如作者发布了2000字符的内容,第一页只显示500字符,点击“下一页”就显示下一段500字符,直到读完所有内容。

示例代码:

export default function Read() {
return (
    <div className="h-screen">
        <div className="flex items-center justify-between  w-screen h-[45px] z-10 bg-white drop-shadow-lg top-0">
            <Link className="flex items-center text-white bg-indigo-800 h-full" href="/"><ArrowLeftIcon
                className="w-6 text-white active:text-white"/><span>Home</span></Link>
            <div className="text-sm font-bold ">
                <p>Book Title will be here</p>
            </div>
            <div className="text-xs font-bold ">
                <p>Page 1</p>
            </div>

            <div className=" ">
                <AdjustmentsHorizontalIcon className="w-7 text-indigo-900 active:text-white"/>
            </div>
        </div>

        <div className="px-1.5 bg-zinc-300 shadow-lg">
            <p className="bg-white px-1">Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Beatae dolor dolores incidunt, ipsa ipsam iure labore maiores
                nobis nulla, odio omnis placeat quod, reiciendis. Cum deleniti
                fugit laudantium repudiandae velit? Dicta dolorem explicabo fuga
                illum incidunt itaque laboriosam magni necessitatibus numquam
                perspiciatis qui quis, quo recusandae, rerum similique temporibus
                tenetur!
                <br/>
                <br/>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Beatae dolor dolores incidunt, ipsa ipsam iure labore maiores
                nobis nulla, odio omnis placeat quod, reiciendis. Cum deleniti
                fugit laudantium repudiandae velit? Dicta dolorem explicabo fuga
                illum incidunt itaque laboriosam magni necessitatibus numquam
                perspiciatis qui quis, quo recusandae, rerum similique temporibus
                tenetur!
                <br/>
                <br/>
                fugit laudantium repudiandae velit? Dicta dolorem explicabo fuga
                illum incidunt itaque laboriosam magni necessitatibus numquam
                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum
                eius eligendi enim expedita ipsum iusto nemo, nisi perspiciatis
                quis vitae!
                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab ad aspernatur aut deleniti doloremque eius eos eveniet, expedita facilis labore magni,
            </p>

        </div>
        <div className="pagination fixed bottom-0 left-0 right-0">
            <div className="active:bg-red-300 rounded-3xl">
                <ArrowLeftCircleIcon className="w-10 text-indigo-600"/>
            </div>

            <div className="active:bg-red-300 rounded-3xl">
                <ArrowRightCircleIcon className="w-10 text-indigo-600"/>
            </div>
        </div>
    </div>
)}

解决方案

核心思路是通过React状态管理当前页码,将完整内容按指定字符数分割为多页片段,再配合分页按钮切换页码渲染对应内容。

修改后的完整代码

import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { ArrowLeftIcon, AdjustmentsHorizontalIcon, ArrowLeftCircleIcon, ArrowRightCircleIcon } from '@heroicons/react/24/outline';

export default function Read() {
  // 核心状态
  const [currentPage, setCurrentPage] = useState(1);
  const charsPerPage = 500; // 每页固定显示500字符
  // 完整文章内容(实际项目中可从接口/Props获取)
  const fullContent = `Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Beatae dolor dolores incidunt, ipsa ipsam iure labore maiores
                nobis nulla, odio omnis placeat quod, reiciendis. Cum deleniti
                fugit laudantium repudiandae velit? Dicta dolorem explicabo fuga
                illum incidunt itaque laboriosam magni necessitatibus numquam
                perspiciatis qui quis, quo recusandae, rerum similique temporibus
                tenetur!
                <br/>
                <br/>
                Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                Beatae dolor dolores incidunt, ipsa ipsam iure labore maiores
                nobis nulla, odio omnis placeat quod, reiciendis. Cum deleniti
                fugit laudantium repudiandae velit? Dicta dolorem explicabo fuga
                illum incidunt itaque laboriosam magni necessitatibus numquam
                perspiciatis qui quis, quo recusandae, rerum similique temporibus
                tenetur!
                <br/>
                <br/>
                fugit laudantium repudiandae velit? Dicta dolorem explicabo fuga
                illum incidunt itaque laboriosam magni necessitatibus numquam
                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum
                eius eligendi enim expedita ipsum iusto nemo, nisi perspiciatis
                quis vitae!
                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab ad aspernatur aut deleniti doloremque eius eos eveniet, expedita facilis labore magni,`;

  // 分割内容为分页数组的工具函数
  const splitContentIntoPages = (content, charsPerPage) => {
    const pages = [];
    let startIndex = 0;
    while (startIndex < content.length) {
      const endIndex = Math.min(startIndex + charsPerPage, content.length);
      pages.push(content.slice(startIndex, endIndex));
      startIndex = endIndex;
    }
    return pages;
  };

  // 存储分页后的内容片段
  const [pages, setPages] = useState([]);
  useEffect(() => {
    setPages(splitContentIntoPages(fullContent, charsPerPage));
  }, [fullContent, charsPerPage]);

  const totalPages = pages.length;

  // 分页切换逻辑
  const goToPreviousPage = () => {
    if (currentPage > 1) setCurrentPage(prev => prev - 1);
  };

  const goToNextPage = () => {
    if (currentPage < totalPages) setCurrentPage(prev => prev + 1);
  };

  // 当前页要渲染的内容
  const currentContent = pages[currentPage - 1] || '';

  return (
    <div className="h-screen">
      <div className="flex items-center justify-between  w-screen h-[45px] z-10 bg-white drop-shadow-lg top-0">
        <Link className="flex items-center text-white bg-indigo-800 h-full" href="/">
          <ArrowLeftIcon className="w-6 text-white active:text-white"/>
          <span>Home</span>
        </Link>
        <div className="text-sm font-bold ">
          <p>Book Title will be here</p>
        </div>
        <div className="text-xs font-bold ">
          <p>Page {currentPage} of {totalPages}</p>
        </div>
        <div className=" ">
          <AdjustmentsHorizontalIcon className="w-7 text-indigo-900 active:text-white"/>
        </div>
      </div>

      <div className="px-1.5 bg-zinc-300 shadow-lg">
        {/* 用dangerouslySetInnerHTML解析内容中的HTML标签 */}
        <p className="bg-white px-1" dangerouslySetInnerHTML={{ __html: currentContent }}></p>
      </div>

      <div className="pagination fixed bottom-0 left-0 right-0 flex justify-center gap-4 py-4 bg-white">
        <div 
          className={`active:bg-red-300 rounded-3xl p-2 ${currentPage === 1 ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
          onClick={goToPreviousPage}
        >
          <ArrowLeftCircleIcon className="w-10 text-indigo-600"/>
        </div>
        <div 
          className={`active:bg-red-300 rounded-3xl p-2 ${currentPage === totalPages ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
          onClick={goToNextPage}
        >
          <ArrowRightCircleIcon className="w-10 text-indigo-600"/>
        </div>
      </div>
    </div>
  );
}

关键细节说明

  1. HTML内容渲染:如果内容包含<br/>等HTML标签,必须用dangerouslySetInnerHTML解析,否则标签会被当作普通文本显示。
  2. 分页按钮状态:给第一页的“上一页”、最后一页的“下一页”添加禁用样式,避免无效点击,提升体验。
  3. 动态适配:如果内容从接口获取,只需替换fullContent为接口返回值,useEffect会自动重新分割分页。
  4. 字符数调整:直接修改charsPerPage的值即可改变每页显示的字符数,灵活适配不同场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 18:01:30