实现一个书本翻页功能

976 阅读3分钟

今天来实现一个可翻页的笔记本~

在线体验

码上掘金

codePen

codepen.io/yongtaozhen…

代码实现

页面配置信息

const pages = [
  {
    front: {
      title: "《千与千寻》",
      text: "少女千寻误入神灵世界,为救变成猪的父母经历磨难,最终以勇气与善意赢得自由。影片隐喻现代社会异化与人性救赎,获奥斯卡最佳动画长片+柏林金熊奖,全球票房超3.5亿美元,成为日本影史巅峰。",
      background: "../images/宫崎骏/千与千寻.jpg",
    },
    back: {
      title: "《天空之城》",
      text: "吉卜力工作室开山之作。少年帕祖与持有飞行石的少女希达为阻止军方夺取古代浮空文明“拉普达”的毁灭性武器,展开空中冒险。影片探讨科技异化与人性贪婪,久石让的配乐成为经典,结尾的“生命之树”象征自然永恒。",
      background: "../images/宫崎骏/天空之城.jpg",
    },
  },
  …………
]

一个页面包含正面和背面,title 是页面的标题,text 是页面的文字内容,background是页面的背景图片。

页面创建

每一页都有正反两面,而且内容都不一样,根据配置信息将页面创建出来,封面和内部页面的样式有所不同,所以我们需要区分开来。

function createPage(info = {}, isCover = false) {
  const pageElement = document.createElement("div");
  const pre = isCover ? "cover" : "page";
  if (info.background) {
    const img = document.createElement("img");
    img.classList.add(`${pre}-image`);
    img.src = info.background;
    pageElement.appendChild(img);
  }
  if (info.title) {
    const titleElement = document.createElement("div");
    titleElement.innerText = info.title;
    titleElement.classList.add(`${pre}-title`);
    pageElement.appendChild(titleElement);
  }
  const textElement = document.createElement("div");
  textElement.innerText = info.text || "";
  textElement.classList.add(`${pre}-text`);
  pageElement.appendChild(textElement);
  return pageElement;
}

function createFrontAndBackPage(pageElement, page = {}, isCover = false) {
  if (!isCover) pageElement.classList.add("page");
  pageElement.classList.add("turn-page");
  const frontPage = createPage(page.front, isCover);
  frontPage.classList.add("front-page");
  const backPage = createPage(page.back, isCover);
  backPage.classList.add("back-page");
  pageElement.appendChild(frontPage);
  pageElement.appendChild(backPage);
}

function createPages(page) {
  pages.reverse().forEach((page, index) => {
    const pageElement = document.createElement("span");
    createFrontAndBackPage(pageElement, page);
    pageElement.setAttribute("data-index", pages.length - index);
    book.appendChild(pageElement);
  });
}

翻页逻辑

翻页限制

  • 向后翻页还没完成时不能往前翻页
if (target.classList.contains("turn")) {
  if (isTurnIng === "turnBack") return;
}
  • 向前翻页还没完成时不能往后翻页
else {
  if (isTurnIng === "turn") return;
}
  • 非当前显示在最前面的页面不允许翻页
if (![showPageNumber, showPageNumber - 1].includes(index)) {
  return;
}

点击翻页

  • 给所有页面都添加上点击事件
function turnPageFn() {
  const coverElements = book.querySelectorAll(".cover");
  const pageElements = book.querySelectorAll(".page");
  pageElements.forEach((page, index) => {
    page.addEventListener("click", pageClick);
  });
  coverElements.forEach((cover, index) => {
    cover.addEventListener("click", pageClick);
  });
}
  • 判断翻页方向
function pageClick(e) {
  const target = e.target;
  const index = parseInt(target.getAttribute("data-index"));
  if (![showPageNumber, showPageNumber - 1].includes(index)) {
    return;
  }
  if (target.classList.contains("turn")) {
    if (isTurnIng === "turnBack") return;
    showPageNumber = index;
    isTurnIng = "turn";
    target.style.transform = "rotateY(-180deg)";
    target.classList.remove("turn");
    target.classList.add("turnBack");
    target.style.zIndex = null;
  } else {
    if (isTurnIng === "turn") return;
    showPageNumber = index + 1;
    isTurnIng = "turnBack";
    target.style.transform = "rotateY(0deg)";
    target.classList.remove("turnBack");
    target.classList.add("turn");
    target.style.zIndex = calZIndex();
  }
  clearTimeout(timer);
  timer = setTimeout(() => {
    isTurnIng = false;
  }, parseFloat(duration) * 1000);
}

css动画

正反页面切换

在翻页过程中需要将正反页面做一个切换,向前翻页到 90° 的时候切换显示正面,往后翻页到 90° 的时候切换显示背面。

.turn-page.turn > div.front-page {
  animation: fadeOut var(--animation-duration) forwards;
}

.turn-page.turnBack > div.front-page {
  animation: fadeIn var(--animation-duration) forwards;
}

.turn-page.turnBack > div.back-page {
  animation: fadeOut var(--animation-duration) forwards;
}

.turn-page.turn > div.back-page {
  animation: fadeIn var(--animation-duration) forwards;
}

/* 淡出动画 */
@keyframes fadeOut {
  0% {
    visibility: visible;
  }
  49% {
    visibility: visible;
  }
  50% {
    visibility: hidden;
  }
  100% {
    visibility: hidden;
  }
}

/* 淡入动画 */
@keyframes fadeIn {
  0% {
    visibility: hidden;
  }
  50% {
    visibility: hidden;
  }
  51% {
    visibility: visible;
  }
  100% {
    visibility: visible;
  }
}

翻页动画

@keyframes turnForward {
  0% {
    z-index: 999;
  }
  40% {
    z-index: 999;
  }
  49% {
    transform: rotateY(-90deg);
  }
  50% {
    transform: rotateY(-90deg);
  }
  100% {
    transform: rotateY(-180deg);
    z-index: null;
  }
}

@keyframes turnBackward {
  0% {
    z-index: 999;
  }
  40% {
    z-index: 999;
  }
  49% {
    transform: rotateY(-90deg);
  }
  50% {
    transform: rotateY(-90deg);
  }
  100% {
    transform: rotateY(0deg);
    z-index: null;
  }
}

源码

gitee

gitee.com/zheng_yongt…

github

github.com/yongtaozhen…


  • 🌟 觉得有帮助的可以点个 star~
  • 🖊 有什么问题或错误可以指出,欢迎 pr~
  • 📬 有什么想要实现的功能或想法可以联系我~

公众号

关注公众号『 前端也能这么有趣 』,获取更多有趣内容。

发送 加群 还可以加入群聊,一起来学习(摸鱼)吧~

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。