class/card3d.js

  1. /**
  2. * 卡片3d动画.
  3. * {@link https://imondo.cn/files/3d.html 查看效果}
  4. * @param { DOM } el 卡片dom集合
  5. * @param { DOM } boxEl 卡片盒子dom
  6. */
  7. class Card3d {
  8. constructor(el, boxEl) {
  9. this.el = el;
  10. this.boxEl = boxEl;
  11. this.page = 0;
  12. this.configCss = [
  13. { deg: 0, origin: "center" }, // 中
  14. { deg: -90, origin: "bottom" }, // 下
  15. { deg: 180, origin: "center" }, // 中
  16. { deg: 90, origin: "top" } // 上
  17. ]
  18. this.group = [];
  19. const boxH = this.boxEl.clientHeight;
  20. this.translateZ = boxH / 2;
  21. this.boxEl.style.transform = `translateZ(-${this.translateZ}px) rotateX(0deg)`;
  22. const pageSize = el.length
  23. this.pageSize = pageSize;
  24. this.maxSize = pageSize - 1;
  25. this.init();
  26. this.initCss();
  27. }
  28. /**
  29. * 初始化事件
  30. * 让第一个,第二个卡片显示,以下的隐藏
  31. */
  32. init() {
  33. this.block({
  34. index: 1,
  35. hideNext: true
  36. });
  37. }
  38. /**
  39. * 初始化样式
  40. * 给每个卡片设置 transform 样式
  41. */
  42. initCss() {
  43. const { el: $els, configCss, translateZ } = this;
  44. this.group = this.sliceGroup($els);
  45. this.arrForch(this.group, function (item, index) {
  46. this.arrForch(item, function (el, i) {
  47. const config = configCss[i];
  48. const sym = i === 0 ? "" : "-";
  49. el.style.transform = `translateZ(${sym}${translateZ}px) rotateX(${config.deg}deg)`
  50. el.style.transformOrigin = `${config.origin}`
  51. // el.style.backfaceVisibility = 'hidden'
  52. })
  53. })
  54. }
  55. /**
  56. * 分割数组
  57. * @param {Array} data 原数组
  58. * @param {Number} size 每一个分割的数组长度
  59. */
  60. sliceGroup(data = [], size = 4) {
  61. const result = [];
  62. for (let i = 0, len = data.length; i < len; i += size) {
  63. result.push(Array.prototype.slice.call(data, i, i + size));
  64. }
  65. return result;
  66. }
  67. /**
  68. * 动画移动
  69. * @param {Function} callback 移动后回调函数
  70. */
  71. move(callback) {
  72. this.rotateX = 90 * this.page;
  73. const nextPage = this.page + 1;
  74. this.block({
  75. index: this.page
  76. });
  77. this.block({
  78. index: nextPage,
  79. hideNext: true
  80. });
  81. if (this.page === this.pageSize) return;
  82. this.boxEl.style.transform = `translateZ(-${this.translateZ}px) rotateX(${this.rotateX}deg)`
  83. callback && callback(this.el[this.page], this.page);
  84. }
  85. /**
  86. * 上滑动
  87. * @param {Function} callback 移动后回调函数
  88. */
  89. pageUp(callback) {
  90. if (this.page >= this.maxSize) {
  91. return;
  92. };
  93. this.page++
  94. this.move(callback);
  95. }
  96. /**
  97. * 下滑动
  98. * @param {Function} callback 移动后回调函数
  99. */
  100. pageDown(callback) {
  101. if (this.page <= 0) {
  102. return;
  103. }
  104. this.page--
  105. this.move(callback);
  106. }
  107. /**
  108. * DOM 元素对象循环
  109. * @param {Array} els DOM 元素集合
  110. * @param {Function} callback 循环回调
  111. */
  112. arrForch(els, callback) {
  113. const _this = this;
  114. Array.prototype.forEach.call(els, function () {
  115. callback.call(_this, ...arguments);
  116. })
  117. }
  118. /**
  119. * 对 DOM 元素设置 display 样式
  120. * @param {Number} index 元素个数下标
  121. * @param {Boolean} hideNext 是否该元素后面的兄弟元素都 display: none,默认值 true
  122. */
  123. block({ index, hideNext = false }) {
  124. if (hideNext) {
  125. this.arrForch(this.el, function (item, i) {
  126. this.el[i].style.opacity = index === i ? 1 : i > index ? 0 : 1;
  127. })
  128. } else {
  129. this.el[index].style.opacity = 1;
  130. }
  131. }
  132. }