interactive-portfolio.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*!
  2. Carto - Interactive portfolio
  3. Created by ThemeZaa
  4. !*/
  5. $(function() {
  6. "use strict";
  7. const container = document.getElementById("stickyImgContainer");
  8. if (typeof (container) != 'undefined' && container != null) {
  9. const sliders = document.querySelector(".slides").querySelectorAll(".slide")
  10. const slidesData = Array.from(sliders).map(item => {
  11. return {
  12. image: "",
  13. title: "title1",
  14. meta: ""
  15. }
  16. })
  17. const createEleWithClass = (tag, className) => {
  18. const ele = document.createElement(tag);
  19. ele.className = className;
  20. return ele;
  21. };
  22. class Slides {
  23. constructor(data) {
  24. this.data = document.querySelectorAll(".slide");
  25. this.container = document.querySelector(".slides");
  26. this.currentIdx = 0;
  27. this.slides = document.querySelectorAll(".slide");
  28. }
  29. mount(container) {
  30. container.appendChild(this.container);
  31. let activeIndex = 0;
  32. for (let i = 0; i < this.slides.length; i++) {
  33. if (activeIndex === i) {
  34. this.slides[i].classList.remove("next");
  35. this.slides[i].classList.remove("prev");
  36. this.slides[i].classList.add("show-meta");
  37. document.querySelector(".bg-overlay").style.backgroundImage = `url("${this.slides[i].dataset.bg}")`
  38. } else {
  39. if (activeIndex > i) {
  40. this.slides[i].classList.remove("next");
  41. this.slides[i].classList.add("prev");
  42. } else {
  43. this.slides[i].classList.add("next");
  44. this.slides[i].classList.remove("prev");
  45. }
  46. }
  47. }
  48. }
  49. onActiveIndexChange(activeIndex) {
  50. this.currentIdx = activeIndex;
  51. for (let i = 0; i < this.slides.length; i++) {
  52. if (activeIndex === i) {
  53. this.slides[i].classList.remove("next");
  54. this.slides[i].classList.remove("prev");
  55. document.querySelector(".bg-overlay").style.backgroundImage = `url("${this.slides[i].dataset.bg}")`
  56. } else {
  57. if (activeIndex > i) {
  58. this.slides[i].classList.remove("next");
  59. this.slides[i].classList.add("prev");
  60. } else {
  61. this.slides[i].classList.add("next");
  62. this.slides[i].classList.remove("prev");
  63. }
  64. }
  65. }
  66. }
  67. onMove(indexFloat) {
  68. this.container.style.transform = `translateY(${(indexFloat * 100) /
  69. this.slides.length}%)`;
  70. }
  71. appear() {
  72. this.container.classList.add("scrolling");
  73. container.classList.add("scrolling");
  74. this.slides[this.currentIdx].classList.remove("show-meta");
  75. }
  76. disperse(activeIndex) {
  77. this.currentIdx = activeIndex;
  78. this.slides[this.currentIdx].classList.add("show-meta");
  79. this.container.classList.remove("scrolling");
  80. container.classList.remove("scrolling");
  81. for (let index = 0; index < this.data.length; index++) {
  82. if (index > activeIndex) {
  83. this.slides[index].classList.add("next");
  84. this.slides[index].classList.remove("prev");
  85. } else if (index < activeIndex) {
  86. this.slides[index].classList.remove("next");
  87. this.slides[index].classList.add("prev");
  88. } else {
  89. this.slides[index].classList.remove("next");
  90. this.slides[index].classList.remove("prev");
  91. }
  92. }
  93. }
  94. }
  95. class Grab {
  96. constructor({
  97. indexSize,
  98. onIndexChange,
  99. onGrabStart,
  100. onGrabMove,
  101. onGrabEnd
  102. }) {
  103. this.onGrabEnd = onGrabEnd;
  104. this.onGrabStart = onGrabStart;
  105. this.onGrabMove = onGrabMove;
  106. this.scroll = {
  107. start: 0,
  108. current: 0,
  109. initial: 0
  110. };
  111. this.listen("mousedown", this.onMouseDown.bind(this));
  112. this.listen("mousemove", this.onMouseMove.bind(this));
  113. this.listen("mouseup", this.onMouseUp.bind(this));
  114. this.listen("touchstart", this.onMouseDown.bind(this), true);
  115. this.listen("touchmove", this.onMouseMove.bind(this), true);
  116. this.listen(["touchend", "touchcancel"], this.onMouseUp.bind(this), true);
  117. }
  118. listen(events, grabListener, isTouch) {
  119. const _this = this;
  120. let mouseListener = function (ev) {
  121. if (ev.type === "mouseout" && ev.relatedTarget != null) return;
  122. grabListener({
  123. y: ev.clientY
  124. });
  125. };
  126. let touchListener = function (ev) {
  127. grabListener({
  128. y: ev.targetTouches[0] ? ev.targetTouches[0].clientY : null
  129. });
  130. };
  131. let listener = mouseListener;
  132. if (isTouch) {
  133. listener = touchListener;
  134. }
  135. if (Array.isArray(events)) {
  136. for (let i = 0; i < events.length; i++) {
  137. window.addEventListener(events[i], listener, false);
  138. }
  139. } else {
  140. window.addEventListener(events, function (event) {
  141. if (event.target.closest(".stickyImageContainer") === container) {
  142. listener(event);
  143. } else {
  144. _this.onMouseUp()
  145. }
  146. }, false);
  147. }
  148. }
  149. onMouseDown(position) {
  150. this.scroll.inital = this.scroll.current;
  151. this.scroll.start = position.y;
  152. this.scroll.current = position.y;
  153. this.scroll.delta = this.scroll.current - this.scroll.start;
  154. this.onGrabStart({
  155. delta: this.scroll.delta,
  156. direction: Math.abs(this.scroll.delta),
  157. current: this.scroll.current,
  158. start: this.scroll.start
  159. });
  160. }
  161. onMouseMove(position) {
  162. if (this.scroll.start) {
  163. this.scroll.current = position.y;
  164. this.scroll.delta = this.scroll.current - this.scroll.start;
  165. this.onGrabMove({
  166. delta: this.scroll.delta,
  167. direction: Math.abs(this.scroll.delta),
  168. current: this.scroll.current,
  169. start: this.scroll.start
  170. });
  171. }
  172. }
  173. onMouseUp() {
  174. if (this.scroll.start) {
  175. this.onGrabEnd({
  176. delta: this.scroll.delta,
  177. direction: Math.abs(this.scroll.delta),
  178. current: this.scroll.current,
  179. start: this.scroll.start
  180. });
  181. this.scroll.start = null;
  182. this.scroll.current = null;
  183. this.scroll.delta = null;
  184. }
  185. }
  186. }
  187. const reach = function ({ from, to, restDelta = 0.01 }) {
  188. let current = Object.assign({}, from);
  189. let keys = Object.keys(from);
  190. let raf = {
  191. current: null
  192. };
  193. let _update = function (update, complete) {
  194. if (keys.length === 0) {
  195. cancelAnimationFrame(raf.current);
  196. raf.current = null;
  197. complete(current);
  198. return;
  199. }
  200. let cacheKeys = keys.slice();
  201. for (var i = keys.length, val, key; i >= 0; i--) {
  202. key = keys[i];
  203. val = current[key] + (to[key] - current[key]) * 0.1;
  204. if (Math.abs(to[key] - val) < restDelta) {
  205. current[key] = to[key];
  206. // Remove key
  207. keys.splice(i, 1);
  208. // Move i down by pne
  209. i--;
  210. } else {
  211. current[key] = val;
  212. }
  213. }
  214. update(current);
  215. raf.current = requestAnimationFrame(_update);
  216. };
  217. return {
  218. start: function ({
  219. update,
  220. complete
  221. }) {
  222. _update = _update.bind(null, update, complete);
  223. raf.current = requestAnimationFrame(_update);
  224. return {
  225. stop: function () {
  226. cancelAnimationFrame(raf.current);
  227. raf.current = null;
  228. }
  229. };
  230. }
  231. };
  232. };
  233. function Showcase(data, options = {}) {
  234. this.data = data;
  235. this.progress = 0;
  236. this.direction = 1;
  237. this.waveIntensity = 0;
  238. this.options = options;
  239. this.index = {
  240. target: 0,
  241. current: 0,
  242. initial: 0,
  243. scrollSize: window.innerHeight / 6,
  244. active: 0
  245. };
  246. this.follower = {
  247. x: 0,
  248. y: 0
  249. };
  250. this.followerSpring = null;
  251. this.slidesSpring = null;
  252. this.grab = new Grab({
  253. onGrabStart: this.onGrabStart.bind(this),
  254. onGrabMove: this.onGrabMove.bind(this),
  255. onGrabEnd: this.onGrabEnd.bind(this)
  256. });
  257. }
  258. function clamp(num, min, max) {
  259. return Math.max(min, Math.min(num, max));
  260. }
  261. Showcase.prototype.onGrabMove = function (scroll) {
  262. this.index.target = clamp(
  263. this.index.initial + scroll.delta / this.index.scrollSize,
  264. -this.data.length + 0.51,
  265. 0.49
  266. );
  267. const index = clamp(Math.round(-this.index.target), 0, this.data.length - 1);
  268. if (this.index.active !== index) {
  269. this.index.active = index;
  270. if (this.options.onActiveIndexChange) {
  271. this.options.onActiveIndexChange(this.index.active);
  272. }
  273. }
  274. if (this.slidesPop) {
  275. this.slidesPop.stop();
  276. }
  277. this.slidesPop = reach({
  278. from: {
  279. index: this.index.current
  280. },
  281. to: {
  282. index: this.index.target
  283. },
  284. restDelta: 0.001
  285. }).start({
  286. update: val => {
  287. if (this.options.onIndexChange) {
  288. this.options.onIndexChange(val.index);
  289. }
  290. this.index.current = val.index;
  291. },
  292. complete: val => {
  293. if (this.options.onIndexChange) {
  294. this.options.onIndexChange(val.index);
  295. }
  296. this.index.current = val.index;
  297. }
  298. });
  299. };
  300. Showcase.prototype.onGrabStart = function () {
  301. if (this.options.onZoomOutStart) {
  302. this.options.onZoomOutStart({
  303. activeIndex: this.index.active
  304. });
  305. }
  306. this.index.initial = this.index.current;
  307. };
  308. Showcase.prototype.snapCurrentToActiveIndex = function () {
  309. if (this.slidesPop) {
  310. this.slidesPop.stop();
  311. }
  312. this.slidesPop = reach({
  313. from: {
  314. index: this.index.current
  315. },
  316. to: {
  317. index: Math.round(this.index.target)
  318. },
  319. restDelta: 0.001
  320. }).start({
  321. complete: () => { },
  322. update: val => {
  323. // this.slides.onMove(val);
  324. if (this.options.onIndexChange) {
  325. this.options.onIndexChange(val.index);
  326. }
  327. this.index.current = val.index;
  328. }
  329. });
  330. };
  331. Showcase.prototype.onGrabEnd = function () {
  332. if (this.options.onFullscreenStart) {
  333. this.options.onFullscreenStart({
  334. activeIndex: this.index.active
  335. });
  336. this.snapCurrentToActiveIndex();
  337. }
  338. };
  339. Showcase.prototype.onResize = function () {
  340. };
  341. const slides = new Slides();
  342. const showcase = new Showcase(slidesData, {
  343. onActiveIndexChange: activeIndex => {
  344. slides.onActiveIndexChange(activeIndex);
  345. },
  346. onIndexChange: index => {
  347. slides.onMove(index);
  348. },
  349. onZoomOutStart: ({ activeIndex }) => {
  350. slides.appear();
  351. },
  352. onZoomOutFinish: ({ activeIndex }) => { },
  353. onFullscreenStart: ({ activeIndex }) => {
  354. slides.disperse(activeIndex);
  355. },
  356. onFullscreenFinish: ({ activeIndex }) => { }
  357. });
  358. slides.mount(container);
  359. }
  360. });