UNPKG

radix-library

Version:

CSS and Javascript library to initialize pages.

911 lines (901 loc) 66.1 kB
/************************************ RADIX - Version : 4.1.8 Copyright 2021 shoalwave and other contributors. Released under the MIT License. https://radix.shoalwave.net/LICENSE ************************************/ let DOMLoaded = false; let windowLoaded = false; window.addEventListener('DOMContentLoaded', () => { DOMLoaded = true }); window.addEventListener('load', () => { windowLoaded = true }); /** * 本体クラス * @typedef radix * @type class * @property {object} option オプション値を格納 * @property {object} modalParts モーダルウィンドウ機能で生成しているエレメントノードたち * @property {object} events 各操作に対して発火しているイベントフックたち * @property {object} icons SVGアイコンの全ノード * @property {boolean} isMobile モバイル端末(タッチ操作可能)か否か * @property {boolean} navState ナビゲーションの開閉状態 * @property {boolean} modalState モーダルウィンドウの開閉状態 * @property {boolean} initialized init() 関数の実行が完了したか * @property {function} init radixの初期化を行う関数 * @property {function} toggleNav ナビゲーション開閉を実行する関数 * @property {function} smoothScroll スムーススクロールを実行する関数 * @property {function} replaceIcon SVGアイコンにエレメントを置換する関数 * @property {function} dragDown ドラッグスクロールのマウスダウン * @property {function} dragMove ドラッグスクロールのマウスムーブ * @property {function} dragUp ドラッグスクロールのマウスアップ * @property {function} modalOpen モーダルウィンドウを開く関数 * @property {function} modalClose モーダルウィンドウを閉じる関数 * @property {function} modalResize モーダルウィンドウの拡縮をする関数 * @property {function} floatRound 浮動小数点数の文字列変換関数 * @property {function} floatCeil 浮動小数点数の文字列変換関数 * @property {function} floatFloor 浮動小数点数の文字列変換関数 * @property {function} preventScroll ページ全体の縦方向スクロール禁止を操作する関数 * @property {function} getEasing イージング関数を取得する関数 */ class Radix { /** * コンストラクタ * @constructor */ constructor(option, mode) { this.version = '4.1.8' const defOption = { timeFrame: 10, preload: { active: false, selector: '', class: 'hide', delay: 200, preventScroll: false }, smoothScroll: { active: true, duration: 600, easing: 'easeInOutExpo', header: '', }, fixExtLink: { active: true }, toggleNav: { active: false, trigger: '', target: '', class: 'opened', preventScroll: false }, icons: { active: true, selector: '.radix-icon' }, dragScroll: { active: true, selector: '.radix-drag', hint: true }, flexFix: { active: true, selector: '.radix-flexfix', inherit: true, min: 0 }, modal: { active: true, selector: '.radix-modal', class: 'white', resizeDuration: 300, resizeEasing: 'easeInOutBack', scaleStep: [0.2, 0.4, 0.6, 0.8, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5], fit: true, drag: true, magnify: true, wrap: false }, scrollAppear: { active: true, selector: '.radix-appear', delay: 200, reset: true, class: 'active' } } const argFlg = this.typeJudge(option) == 'object' ? true : false; Object.keys(defOption).forEach(key => { if (mode === false && key != 'timeFrame') { defOption[key].active = false; } if (argFlg) { Object.assign(defOption[key], option[key]); } }); this.option = defOption; // インスタンス変数 this.navState = false; this.modalState = false; this.initialized = false; this.isMobile = typeof window.ontouchstart === "undefined" ? false : true; this.DOM_ROOTS = document.querySelectorAll('html,body'); this.events = { beforeInitialize: new CustomEvent('_radixInit'), afterInitialize: new CustomEvent('radixInit_'), beforeScroll: new CustomEvent('_radixScroll'), afterScroll: new CustomEvent('radixScroll_'), beforeNavOpen: new CustomEvent('_radixNavOpen'), afterNavOpen: new CustomEvent('radixNavOpen_'), beforeNavClose: new CustomEvent('_radixNavClose'), afterNavClose: new CustomEvent('radixNavClose_'), beforeModalOpen: new CustomEvent('_radixModalOpen'), afterModalOpen: new CustomEvent('radixModalOpen_'), beforeModalClose: new CustomEvent('_radixModalClose'), afterModalClose: new CustomEvent('radixModalClose_'), } } /** * DOMロード時に実行し初期化する関数 */ init() { const self = this; new Promise(resolve => { // Speed 0 document.dispatchEvent(self.events.beforeInitialize); if (DOMLoaded) { resolve(); } else { window.addEventListener('DOMContentLoaded', resolve); } }).then(() => { // Speed 1 return new Promise(resolve => { // preload display if (self.option.preload.active) { if (self.option.preload.preventScroll) self.preventScroll(true); let preloader = self.option.preload.selector.length > 0 ? document.querySelectorAll(self.option.preload.selector) : []; window.addEventListener('load', () => { if (self.option.preload.active && preloader && self.initialized) { setTimeout(() => { if (self.option.preload.preventScroll) self.preventScroll(false); preloader.forEach(pl => { pl.classList.add(self.option.preload.class); }); }, self.option.preload.delay); } }); document.addEventListener('radixInit_', () => { self.initialized = true; if (self.option.preload.active && preloader.length > 0 && windowLoaded) { setTimeout(() => { if (self.option.preload.preventScroll) self.preventScroll(false); preloader.forEach(pl => { pl.classList.add(self.option.preload.class); }); }, self.option.preload.delay); } }); } // SVG insert if (self.option.icons.active) { const iconSources = { cross: { viewbox: '0 0 100 100', path: 'M 2 17 L 17 2 L 50 35 L 83 2 L 98 17 L 65 50 L 98 83 L 83 98 L 50 65 L 17 98 L 2 83 L 35 50 L 2 17 Z', fill: true, stroke: 0, linejoin: 'miter', linecap: 'butt' }, angle_top: { viewbox: '0 0 100 100', path: 'M 50 19 L 97 66 L 84 79 L 50 45 L 16 79 L 3 66 L 50 19 Z', fill: true, stroke: 0 }, angle_right: { viewbox: '0 0 100 100', path: 'M 81 50 L 34 97 L 21 84 L 55 50 L 21 16 L 34 3 L 81 50 Z', fill: true, stroke: 0 }, angle_bottom: { viewbox: '0 0 100 100', path: 'M 50 81 L 97 34 L 84 21 L 50 55 L 16 21 L 3 34 L 50 81 Z', fill: true, stroke: 0 }, angle_left: { viewbox: '0 0 100 100', path: 'M 19 50 L 66 3 L 79 16 L 45 50 L 79 84 L 66 97 L 19 50 Z', fill: true, stroke: 0 }, arrow_lr: { viewbox: '0 0 100 100', path: 'M 10 50 L 35 20 L 35 42 L 65 42 L 65 20 L 90 50 L 65 80 L 65 58 L 35 58 L 35 80 L 10 50 Z', fill: true, stroke: 0 }, arrow_tb: { viewbox: '0 0 100 100', path: 'M 50 10 L 80 35 L 58 35 L 58 65 L 80 65 L 50 90 L 20 65 L 42 65 L 42 35 L 20 35 L 50 10 Z', fill: true, stroke: 0 }, magnifying_glass: { viewbox: '0 0 500 500', path: 'M 120 195 C 120 92.896 202.896 10 305 10 C 407.104 10 490 92.896 490 195 C 490 297.104 407.104 380 305 380 C 202.896 380 120 297.104 120 195 Z M 180 195 C 179.99 186.52 180.85 178.06 182.55 169.75 C 184.18 161.76 186.61 153.94 189.8 146.42 C 196.1 131.55 205.21 118.05 216.64 106.64 C 228.05 95.21 241.55 86.1 256.42 79.8 C 263.94 76.61 271.76 74.18 279.76 72.54 C 288.07 70.85 296.52 69.99 305 70 C 313.48 69.99 321.93 70.85 330.24 72.54 C 338.24 74.18 346.06 76.61 353.58 79.8 C 368.45 86.1 381.95 95.21 393.36 106.64 C 404.79 118.05 413.9 131.55 420.2 146.42 C 423.39 153.94 425.82 161.76 427.46 169.76 C 429.15 178.07 430.01 186.52 430 195 C 430.01 203.48 429.15 211.93 427.46 220.24 C 425.82 228.24 423.39 236.06 420.2 243.58 C 413.9 258.45 404.79 271.95 393.36 283.36 C 381.95 294.79 368.45 303.9 353.58 310.2 C 346.06 313.39 338.24 315.82 330.24 317.46 C 321.93 319.15 313.48 320.01 305 320 C 296.52 320.01 288.07 319.15 279.76 317.46 C 271.76 315.82 263.94 313.39 256.42 310.2 C 241.55 303.9 228.05 294.79 216.64 283.36 C 205.21 271.95 196.1 258.45 189.8 243.58 C 186.61 236.06 184.18 228.24 182.54 220.24 C 180.85 211.93 179.99 203.48 180 195 Z M 13 490 L 13 413.148 L 126.148 300 L 203 376.852 L 89.852 490 L 13 490 Z', fill: true, fillRule: "evenodd", stroke: 0 }, zoom_in: { viewbox: '0 0 500 500', path: 'M 120 195 C 120 92.896 202.896 10 305 10 C 407.104 10 490 92.896 490 195 C 490 297.104 407.104 380 305 380 C 202.896 380 120 297.104 120 195 Z M 180 195 C 179.99 186.52 180.85 178.06 182.55 169.75 C 184.18 161.76 186.61 153.94 189.8 146.42 C 196.1 131.55 205.21 118.05 216.64 106.64 C 228.05 95.21 241.55 86.1 256.42 79.8 C 263.94 76.61 271.76 74.18 279.76 72.54 C 288.07 70.85 296.52 69.99 305 70 C 313.48 69.99 321.93 70.85 330.24 72.54 C 338.24 74.18 346.06 76.61 353.58 79.8 C 368.45 86.1 381.95 95.21 393.36 106.64 C 404.79 118.05 413.9 131.55 420.2 146.42 C 423.39 153.94 425.82 161.76 427.46 169.76 C 429.15 178.07 430.01 186.52 430 195 C 430.01 203.48 429.15 211.93 427.46 220.24 C 425.82 228.24 423.39 236.06 420.2 243.58 C 413.9 258.45 404.79 271.95 393.36 283.36 C 381.95 294.79 368.45 303.9 353.58 310.2 C 346.06 313.39 338.24 315.82 330.24 317.46 C 321.93 319.15 313.48 320.01 305 320 C 296.52 320.01 288.07 319.15 279.76 317.46 C 271.76 315.82 263.94 313.39 256.42 310.2 C 241.55 303.9 228.05 294.79 216.64 283.36 C 205.21 271.95 196.1 258.45 189.8 243.58 C 186.61 236.06 184.18 228.24 182.54 220.24 C 180.85 211.93 179.99 203.48 180 195 Z M 13 490 L 13 413.148 L 126.148 300 L 203 376.852 L 89.852 490 L 13 490 Z M 205 160 L 270 160 L 270 95 L 340 95 L 340 160 L 405 160 L 405 230 L 340 230 L 340 295 L 270 295 L 270 230 L 205 230 L 205 160 Z', fill: true, fillRule: "evenodd", stroke: 0 }, zoom_out: { viewbox: '0 0 500 500', path: 'M 120 195 C 120 92.896 202.896 10 305 10 C 407.104 10 490 92.896 490 195 C 490 297.104 407.104 380 305 380 C 202.896 380 120 297.104 120 195 Z M 180 195 C 179.99 186.52 180.85 178.06 182.55 169.75 C 184.18 161.76 186.61 153.94 189.8 146.42 C 196.1 131.55 205.21 118.05 216.64 106.64 C 228.05 95.21 241.55 86.1 256.42 79.8 C 263.94 76.61 271.76 74.18 279.76 72.54 C 288.07 70.85 296.52 69.99 305 70 C 313.48 69.99 321.93 70.85 330.24 72.54 C 338.24 74.18 346.06 76.61 353.58 79.8 C 368.45 86.1 381.95 95.21 393.36 106.64 C 404.79 118.05 413.9 131.55 420.2 146.42 C 423.39 153.94 425.82 161.76 427.46 169.76 C 429.15 178.07 430.01 186.52 430 195 C 430.01 203.48 429.15 211.93 427.46 220.24 C 425.82 228.24 423.39 236.06 420.2 243.58 C 413.9 258.45 404.79 271.95 393.36 283.36 C 381.95 294.79 368.45 303.9 353.58 310.2 C 346.06 313.39 338.24 315.82 330.24 317.46 C 321.93 319.15 313.48 320.01 305 320 C 296.52 320.01 288.07 319.15 279.76 317.46 C 271.76 315.82 263.94 313.39 256.42 310.2 C 241.55 303.9 228.05 294.79 216.64 283.36 C 205.21 271.95 196.1 258.45 189.8 243.58 C 186.61 236.06 184.18 228.24 182.54 220.24 C 180.85 211.93 179.99 203.48 180 195 Z M 13 490 L 13 413.148 L 126.148 300 L 203 376.852 L 89.852 490 L 13 490 Z M 205 160 L 405 160 L 405 230 L 205 230 L 205 160 Z', fill: true, fillRule: "evenodd", stroke: 0 } }; self.icons = {}; Object.keys(iconSources).forEach(iconName => { let iconData = iconSources[iconName]; let svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svgIcon.setAttribute('viewBox', iconData.viewbox); svgIcon.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); svgIcon.classList.add('radix-icon'); svgIcon.classList.add('rdx-icon-' + iconName); let iconPath = document.createElementNS('http://www.w3.org/2000/svg', 'path'); iconPath.setAttribute('d', iconData.path); if (iconData.fill) { iconPath.setAttribute('fill', 'currentColor'); if (iconData.fillRule !== undefined) iconPath.setAttribute('fill-rule', iconData.fillRule); } else { iconPath.setAttribute('fill', 'none'); } if (iconData.stroke === 0) { iconPath.setAttribute('stroke', 'none'); } else { iconPath.setAttribute('stroke', 'currentColor'); iconPath.setAttribute('stroke-linejoin', iconData.linejoin); iconPath.setAttribute('stroke-linecap', iconData.linecap); } iconPath.setAttribute('stroke', 'none'); svgIcon.appendChild(iconPath); self.icons[iconName] = svgIcon; }); // hamburger Menu let hmbgr = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); let hmbgrLine1 = document.createElementNS('http://www.w3.org/2000/svg', 'line'); let hmbgrLine2 = document.createElementNS('http://www.w3.org/2000/svg', 'line'); let hmbgrLine3 = document.createElementNS('http://www.w3.org/2000/svg', 'line'); hmbgr.setAttribute('viewBox', '0 0 50 50'); hmbgr.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); hmbgr.classList.add('radix-icon'); hmbgr.classList.add('rdx-icon-hamburger'); hmbgrLine1.setAttribute('x1', 5); hmbgrLine1.setAttribute('y1', 13); hmbgrLine1.setAttribute('x2', 45); hmbgrLine1.setAttribute('y2', 13); hmbgrLine1.classList.add('bar1'); hmbgrLine2.setAttribute('x1', 5); hmbgrLine2.setAttribute('y1', 25); hmbgrLine2.setAttribute('x2', 45); hmbgrLine2.setAttribute('y2', 25); hmbgrLine2.classList.add('bar2'); hmbgrLine3.setAttribute('x1', 5); hmbgrLine3.setAttribute('y1', 37); hmbgrLine3.setAttribute('x2', 45); hmbgrLine3.setAttribute('y2', 37); hmbgrLine3.classList.add('bar3'); hmbgr.appendChild(hmbgrLine1); hmbgr.appendChild(hmbgrLine2); hmbgr.appendChild(hmbgrLine3); self.icons.hamburger = hmbgr; // svg insert let iconCalls = document.querySelectorAll(self.option.icons.selector); if (iconCalls) { iconCalls.forEach(iconCall => { let innerText = iconCall.textContent; self.replaceIcon(iconCall, innerText); }); } } resolve(); }); }).then(() => { // Speed 2 return new Promise(resolve => { // Smooth scroll and Fix external links if (self.option.smoothScroll.active || self.option.fixExtLink.active) { const links = document.querySelectorAll('a'); let scrollFrom = null; let scrollTo = null; links.forEach(link => { if (!link.hasAttribute('href') || link.getAttribute('href').length == 0) { link.setAttribute('href', '#'); } let linkHref = link.getAttribute('href'); // Smooth scroll if (self.option.smoothScroll.active) { let firstLetter = linkHref.substring(0, 1); let hrefTarget = linkHref.substring(1); if (firstLetter === '#') { link.addEventListener('click', event => { event.preventDefault(); const target = hrefTarget.length > 0 ? document.getElementById(hrefTarget) : document.body; const uniqueEasing = link.dataset.scrollEasing; const uniqueDuration = link.dataset.scrollDuration; const headerElm = !self.option.smoothScroll.header ? null : document.querySelector(self.option.smoothScroll.header); const headerFixed = headerElm ? window.getComputedStyle(headerElm).getPropertyValue('position') : null; const headerHeight = headerFixed === 'fixed' ? headerElm.offsetHeight : 0; scrollFrom = window.scrollY; scrollTo = window.scrollY + target.getBoundingClientRect().top - headerHeight; self.smoothScroll(scrollFrom, scrollTo, uniqueDuration, uniqueEasing); }); } } // Auto fill target blank if (self.option.fixExtLink.active) { let host = null; if (linkHref.match(/.+\.pdf$/)) { link.setAttribute('target', '_blank'); link.setAttribute('rel', 'noopener'); link.classList.add('rdx-pdf'); } else if (linkHref.match(/^http/)) { host = window.location.hostname; const linkHost = linkHref.replace(/\\/g, '/').match(/\/\/([^/]*)/) if (host === '' || !!linkHost && linkHost[1] !== host) { link.setAttribute('target', '_blank'); link.setAttribute('rel', 'noopener'); link.classList.add('rdx-extlink'); } } } }); } // Toggle menu if (self.option.toggleNav.active && self.option.toggleNav.trigger !== '' && self.option.toggleNav.target !== '') { self.navState = false; let toggleTrigger = document.querySelectorAll(self.option.toggleNav.trigger); toggleTrigger.forEach(t => { t.addEventListener('click', () => { self.toggleNav(); }); }); } // drag scroll if (self.option.dragScroll.active && self.option.dragScroll.selector !== '') { let dragScrollWrapper = Array.from(document.querySelectorAll(self.option.dragScroll.selector)); let scrollHint = document.createElement('div'); scrollHint.classList.add('rdx-drag-hint'); let scrollHintInner = document.createElement('div'); scrollHintInner.classList.add('rdx-drag-hint-inner'); scrollHint.appendChild(scrollHintInner); scrollHintInner.innerHTML = '<div class="rdx-drag-hint-text">DRAG</div>'; scrollHintInner.prepend(self.icons.arrow_lr.cloneNode(true)); if (dragScrollWrapper.length > 0) { dragScrollWrapper.forEach(e => { e.style.overflow = 'auto'; e.style.position = 'relative'; e.addEventListener('mousedown', v => { self.dragDown(e, v) }, false); if (e.scrollWidth > e.clientWidth && self.option.dragScroll.hint) { e.appendChild(scrollHint.cloneNode(true)); e.addEventListener('scroll', () => { let thisHint = e.querySelector('.rdx-drag-hint'); if (thisHint !== null) { thisHint.classList.add('hide'); } }); } }); } document.addEventListener('touchmove', v => { self.dragMove(v) }, false); document.addEventListener('mousemove', v => { self.dragMove(v) }, false); document.addEventListener('mouseup', v => { self.dragUp(v) }, false); } // flex fix if (self.option.flexFix.active) { let flexFix = document.querySelectorAll(self.option.flexFix.selector); if (flexFix.length > 0) { let inherit = self.option.flexFix.inherit; flexFix.forEach(e => { let childArr = Array.from(e.children); let dummyChild = childArr[0].cloneNode(inherit); dummyChild.classList.add('rdx-dummy-item'); let count = self.option.flexFix.min > childArr.length ? self.option.flexFix.min : childArr.length; for (let i = 0; i < count; i++) { let dummyClone = dummyChild.cloneNode(inherit); e.appendChild(dummyClone); } }); } } // scroll appear if (self.option.scrollAppear.active) { const appearItems = document.querySelectorAll(self.option.scrollAppear.selector); if (appearItems.length > 0) { appearItems.forEach(item => { item.classList.add('rdx-scroll-appear'); }); window.addEventListener('scroll', () => { let windowHeight = window.innerHeight; appearItems.forEach(appearItem => { let resetFlg = appearItem.hasAttribute('data-appear-reset') ? appearItem.dataset.appearReset : self.option.scrollAppear.reset; let activeClass = appearItem.hasAttribute('data-appear-class') ? appearItem.dataset.appearClass : self.option.scrollAppear.class; let delay = appearItem.hasAttribute('data-appear-delay') ? appearItem.dataset.appearDelay : self.option.scrollAppear.delay; let modeNum = appearItem.hasAttribute('data-appear-fixed') ? appearItem.dataset.appearFixed : null; if (modeNum === null) { let itemRect = appearItem.getBoundingClientRect(); let itemHeight = appearItem.offsetHeight; if (itemRect.top < windowHeight * .9 && itemRect.top + itemHeight > windowHeight * .1) { setTimeout(() => { appearItem.classList.add(activeClass); }, delay); } else if (resetFlg) { setTimeout(() => { appearItem.classList.remove(activeClass); }, delay); } } else { if (window.pageYOffset > modeNum) { setTimeout(() => { appearItem.classList.add(activeClass); }, delay); } else if (resetFlg) { setTimeout(() => { appearItem.classList.remove(activeClass); }, delay); } } }); }); } } resolve(); }); }).then(() => { // Speed 3 return new Promise(resolve => { // modal if (self.option.modal.active) { let modals = document.querySelectorAll(self.option.modal.selector); if (modals.length > 0) { self.modalParts = { viewport: document.createElement('div'), area: document.createElement('div'), wrapper: document.createElement('div'), frame: document.createElement('div'), content: document.createElement('div'), closeButton: self.icons.cross.cloneNode(true), toggles: document.createElement('div'), magnifier: document.createElement('div'), magnify: self.option.modal.magnify, enlarge: self.icons.zoom_in.cloneNode(true), shrink: self.icons.zoom_out.cloneNode(true), scaleDisp: document.createElement('div'), scaleSelector: document.createElement('ul'), scale: self.option.modal.defaultScale, size: { width: 0, height: 0 }, class: self.option.modal.class, drag: self.option.modal.drag, dragEvent: v => { self.dragDown(self.modalParts.wrapper, v) }, fit: self.option.modal.fit, duration: self.option.modal.resizeDuration, easing: self.option.modal.resizeEasing } self.modalParts.viewport.classList.add('rdx-modal-viewport'); self.modalParts.area.classList.add('rdx-modal-area'); self.modalParts.wrapper.classList.add('rdx-modal-wrapper'); self.modalParts.content.classList.add('rdx-modal-content'); self.modalParts.frame.classList.add('rdx-modal-frame'); self.modalParts.toggles.classList.add('rdx-modal-toggles'); self.modalParts.viewport.appendChild(self.modalParts.toggles); self.modalParts.viewport.appendChild(self.modalParts.area); self.modalParts.area.appendChild(self.modalParts.wrapper); self.modalParts.wrapper.appendChild(self.modalParts.frame); self.modalParts.frame.appendChild(self.modalParts.content); self.modalParts.magnifier.classList.add('rdx-modal-magnifier'); self.modalParts.enlarge.classList.add('rdx-modal-enlarge'); self.modalParts.shrink.classList.add('rdx-modal-shrink'); self.modalParts.scaleDisp.classList.add('rdx-modal-scale'); self.modalParts.toggles.appendChild(self.modalParts.magnifier); self.modalParts.magnifier.appendChild(self.modalParts.scaleDisp); self.modalParts.magnifier.appendChild(self.modalParts.enlarge); self.modalParts.magnifier.appendChild(self.modalParts.shrink); self.modalParts.closeButton.classList.add('rdx-modal-close'); self.modalParts.toggles.appendChild(self.modalParts.closeButton); self.modalParts.scaleSelector.classList.add('rdx-modal-scaler'); self.modalParts.magnifier.appendChild(self.modalParts.scaleSelector); document.body.appendChild(self.modalParts.viewport); self.modalParts.scaleDisp.addEventListener('click', event => { event.preventDefault(); let i = self.option.modal.scaleStep.indexOf(self.modalParts.scale); self.modalParts.scaleSelector.scrollTop = 40 * i; self.modalParts.scaleSelector.classList.add('active'); }); self.option.modal.scaleStep.forEach(scale => { let scaleItem = document.createElement('li'); scaleItem.innerHTML = self.floatRound(scale, 1) + 'x'; scaleItem.dataset.scale = scale; self.modalParts.scaleSelector.appendChild(scaleItem); scaleItem.addEventListener('click', event => { event.preventDefault(); self.modalResize(scaleItem.dataset.scale); self.modalParts.scale = scale; }); }); self.modalParts.enlarge.addEventListener('click', event => { event.preventDefault(); let i = self.option.modal.scaleStep.indexOf(self.modalParts.scale); let aftScale = i < self.option.modal.scaleStep.length - 1 ? self.option.modal.scaleStep[i + 1] : self.option.modal.scaleStep[i]; self.modalResize(aftScale); self.modalParts.scale = aftScale; }); self.modalParts.shrink.addEventListener('click', event => { event.preventDefault(); let i = self.option.modal.scaleStep.indexOf(self.modalParts.scale); let aftScale = i > 0 ? self.option.modal.scaleStep[i - 1] : self.option.modal.scaleStep[i]; self.modalResize(aftScale); self.modalParts.scale = aftScale; }); modals.forEach(modal => { let targets = []; if (modal.hasAttribute('data-modal-target')) { targets = document.querySelectorAll(modal.dataset.modalTarget); if (targets) { targets.forEach(target => { target.classList.add('rdx-modal-source'); }); } } else { targets = [modal]; modal.classList.add('rdx-modal-source'); } modal.addEventListener('click', event => { event.preventDefault(); let duration = modal.hasAttribute('data-modal-duration') ? modal.dataset.modalDuration : null; let easing = modal.hasAttribute('data-modal-easing') ? modal.dataset.modalEasing : null; let scale = modal.hasAttribute('data-modal-scale') ? modal.dataset.modalScale : null; let drag = modal.hasAttribute('data-modal-drag') ? modal.dataset.modalDrag : null; let magnify = modal.hasAttribute('data-modal-magnify') ? modal.dataset.modalMagnify : null; let fit = modal.hasAttribute('data-modal-fit') ? modal.dataset.modalFit : null; let Class = modal.hasAttribute('data-modal-class') ? modal.dataset.modalClass : null; let wrap = modal.hasAttribute('data-modal-wrap') ? modal.dataset.modalWrap : null; self.modalOpen(targets, Class, duration, easing, scale, drag, magnify, fit, wrap); }); }); self.modalParts.viewport.addEventListener('mouseup', event => { let clicked = event.target; if (!clicked.classList.contains('rdx-modal-item')) { if (clicked.closest('.rdx-modal-content') === null && clicked.closest('.rdx-modal-toggles') === null) { self.modalClose(); } } }, false); self.modalParts.closeButton.addEventListener('click', () => { self.modalClose() }, false); } } resolve(); }); }).then(() => { // Speed 4 document.dispatchEvent(self.events.afterInitialize); }); }; /** * スムーススクロール * @param {float} scrollFrom スクロールの始点 * @param {float} scrollTo スクロールの終点 * @param {int} duration スクロールにかける時間(ミリ秒) * @param {string} easingName スクロールアニメーションのイージング */ smoothScroll(scrollFrom, scrollTo, duration, easingName) { const self = this; scrollTo = scrollTo < 0 ? 0 : scrollTo; const changeVal = scrollTo - scrollFrom; duration = duration === undefined ? self.option.smoothScroll.duration : duration; easingName = easingName === undefined ? self.option.smoothScroll.easing : easingName; let easing = self.getEasing(easingName); let cnt = 0; let timer = null; document.dispatchEvent(self.events.beforeScroll); let moveAnimate = () => { cnt++; let elapsedTime = cnt * self.option.timeFrame; let pos = easing(elapsedTime, scrollFrom, changeVal, duration); window.scrollTo(0, pos); if (elapsedTime > duration) { window.scrollTo(0, scrollTo); clearInterval(timer); document.dispatchEvent(self.events.afterScroll); } } timer = setInterval(moveAnimate, self.option.timeFrame); }; /** * ページスクロールのオンオフ * @param {boolean} mode 禁止する(true)、禁止しない(false) */ preventScroll(mode) { this.DOM_ROOTS.forEach(e => { if (mode) { e.style.overflowY = 'hidden'; } else { e.style.overflowY = 'initial'; } }); }; /** * ナビゲーション開閉 * @param {boolean | string} mode 開ける(false)か閉じる(true)か */ toggleNav(mode) { const self = this; let toggleTrigger = document.querySelectorAll(self.option.toggleNav.trigger); let toggleTarget = document.querySelectorAll(self.option.toggleNav.target); if (mode === undefined) { if (self.navState) { document.dispatchEvent(self.events.beforeNavClose); if (self.option.toggleNav.preventScroll) { self.preventScroll(false); } self.navState = false; toggleTrigger.forEach(t => { t.classList.remove(self.option.toggleNav.class); }); toggleTarget.forEach(t => { t.classList.remove(self.option.toggleNav.class); }); document.dispatchEvent(self.events.afterNavClose); } else { document.dispatchEvent(self.events.beforeNavOpen); if (self.option.toggleNav.preventScroll) { self.preventScroll(true); } self.navState = true; toggleTrigger.forEach(t => { t.classList.add(self.option.toggleNav.class); }); toggleTarget.forEach(t => { t.classList.add(self.option.toggleNav.class); }); document.dispatchEvent(self.events.afterNavOpen); } } else { if (mode === true || mode === 'close') { document.dispatchEvent(self.events.beforeNavClose); if (self.option.toggleNav.preventScroll) { self.preventScroll(false); } self.navState = false; toggleTrigger.forEach(t => { t.classList.remove(self.option.toggleNav.class); }); toggleTarget.forEach(t => { t.classList.remove(self.option.toggleNav.class); }); document.dispatchEvent(self.events.afterNavClose); } else if (mode === false || mode === 'open') { document.dispatchEvent(self.events.beforeNavOpen); if (self.option.toggleNav.preventScroll) { self.preventScroll(true); } self.navState = true; toggleTrigger.forEach(t => { t.classList.add(self.option.toggleNav.class); }); toggleTarget.forEach(t => { t.classList.add(self.option.toggleNav.class); }); document.dispatchEvent(self.events.afterNavOpen); } } }; /** * SVG iconに置き換える * @param {element} target SVGに置き換える対象 * @param {element} iconName アイコンの名前 */ replaceIcon(target, iconName) { const self = this; iconName = iconName === undefined ? target.innerText : iconName; if (Object.keys(self.icons).includes(iconName)) { target.replaceWith(self.icons[iconName].cloneNode(true)); } else { console.log('' + iconName + ' is not icon name.'); } }; /** * ドラッグスクロールのマウスダウンイベント * @param {elemnt} e スクロール領域のエレメント * @param {event} v マウスダウンイベント */ dragDown(e, v) { if (!self.isMobile) { e.style.cursor = 'move'; e.setAttribute('rdx-drag-on', true); e.setAttribute('rdx-drag-scrolled-x', e.scrollLeft); e.setAttribute('rdx-drag-scrolled-y', e.scrollTop); e.setAttribute('rdx-drag-click-x', v.clientX); e.setAttribute('rdx-drag-click-y', v.clientY); } }; /** * ドラッグスクロールのマウスムーブイベント * @param {event} v マウスムーブイベント */ dragMove(v) { let target = document.querySelector('[rdx-drag-on="true"]'); if (target !== null) { v.preventDefault(); target.scrollLeft = Number(target.getAttribute('rdx-drag-scrolled-x')) + Number(target.getAttribute('rdx-drag-click-x')) - v.clientX; target.scrollTop = Number(target.getAttribute('rdx-drag-scrolled-y')) + Number(target.getAttribute('rdx-drag-click-y')) - v.clientY; } }; /** * ドラッグスクロールのマウスアップイベント * @param {event} v マウスアップイベント */ dragUp(v) { v.preventDefault(); v.stopImmediatePropagation(); let target = document.querySelector('[rdx-drag-on="true"]'); if (target) { target.style.cursor = ''; target.setAttribute('rdx-drag-on', false); } }; /** * モーダルを開く * @param {array} targets 開く対象のエレメントの配列 * @param {string} Class viewport に追加するCSSクラス * @param {int} duration 拡縮にかける時間 * @param {string} easing 拡縮アニメーションのイージング * @param {float} scale 開いたときの拡大率 * @param {boolean} drag ドラッグスクロールを可能にするか * @param {boolean} magnify 拡縮ボタンを表示するか * @param {boolean} fit 展開時にスケールを自動調整するか * @param {boolean} wrap 折り返しモードで開くか */ modalOpen(targets, Class, duration, easing, scale, drag, magnify, fit, wrap) { const self = this; if (self.modalState) return; new Promise(resolve => { document.dispatchEvent(self.events.beforeModalOpen); resolve(); }).then(() => { return new Promise(resolve => { self.modalParts.content.innerHTML = ''; self.modalParts.magnify = magnify !== null ? magnify : self.option.modal.magnify; self.modalParts.drag = drag !== null ? drag : self.option.modal.drag; self.modalParts.fit = fit !== null ? fit : self.option.modal.fit; self.modalParts.duration = duration === undefined ? self.option.modal.resizeDuration : duration; self.modalParts.easing = easing === undefined ? self.option.modal.resizeEasing : easing; self.modalParts.wrap = wrap === undefined ? self.option.modal.wrap : wrap; let classArr = Class !== null ? Class : self.option.modal.class; classArr = String(classArr).split(' '); self.modalParts.class = classArr; self.preventScroll(true); resolve(); }); }).then(() => { return new Promise(resolve => { targets.forEach(target => { let modalClone = target.cloneNode(true); modalClone.classList.remove('rdx-modal-source'); modalClone.classList.add('rdx-modal-item'); self.modalParts.content.appendChild(modalClone); }); resolve(); }); }).then(() => { return new Promise(resolve => { self.modalParts.magnifier.style.display = self.str2bool(self.modalParts.magnify) && !self.str2bool(self.modalParts.wrap) ? 'flex' : 'none'; self.modalParts.size = { width: self.modalParts.content.offsetWidth, height: self.modalParts.content.offsetHeight }; resolve(); }); }).then(() => { return new Promise(resolve => { self.modalParts.scale = 1; if (scale !== null) { self.modalParts.scale = self.floatRound(scale, 1); } else if (self.str2bool(self.modalParts.fit)) { let areaHeight = self.modalParts.area.clientHeight; let areaWidth = self.modalParts.area.clientWidth; self.modalParts.scale = self.option.modal.scaleStep[0]; for (let i = 0; i < self.option.modal.scaleStep.length; i++) { if (self.modalParts.size.width * self.option.modal.scaleStep[i] > areaWidth || self.modalParts.size.height * self.option.modal.scaleStep[i] > areaHeight) break; self.modalParts.scale = self.option.modal.scaleStep[i]; } } if (self.str2bool(self.modalParts.drag) && !self.str2bool(self.modalParts.wrap)) { self.modalParts.wrapper.addEventListener('mousedown', self.modalParts.dragEvent, false); } else { self.modalParts.wrapper.removeEventListener('mousedown', self.modalParts.dragEvent, false); } resolve(); }); }).then(() => { return new Promise(resolve => { if (self.str2bool(self.modalParts.wrap)) { self.modalParts.content.style.maxHeight = '100%'; self.modalParts.content.style.maxWidth = '100%'; } else { self.modalParts.content.style.transform = 'scale(' + self.modalParts.scale + ')'; self.modalParts.wrapper.style.height = 'min(' + self.floatCeil(self.modalParts.size.height * self.modalParts.scale, 0) + 'px, 100%)'; self.modalParts.wrapper.style.width = 'min(' + self.floatCeil(self.modalParts.size.width * self.modalParts.scale, 0) + 'px, 100%)'; self.modalParts.scaleDisp.innerHTML = self.floatRound(self.modalParts.scale, 1) + 'x'; self.modalParts.frame.style.height = self.floatCeil(self.modalParts.size.height * self.modalParts.scale, 0) + 'px'; self.modalParts.frame.style.width = self.floatCeil(self.modalParts.size.width * self.modalParts.scale, 0) + 'px'; } self.modalParts.class.forEach(c => { self.modalParts.viewport.classList.add(c); }); self.modalParts.viewport.classList.add('active'); self.modalState = true; resolve(); }); }).then(() => { document.dispatchEvent(self.events.afterModalOpen); }); }; /** * モーダルを閉じる */ modalClose() { const self = this; if (!self.modalState) return; if (self.modalParts.wrapper.getAttribute('rdx-drag-on') === 'true') return; new Promise(resolve => { document.dispatchEvent(self.events.beforeModalClose); resolve(); }).then(() => { return new Promise(resolve => { self.modalParts.scaleSelector.classList.remove('active'); self.modalParts.content.innerHTML = ''; self.preventScroll(false); self.modalParts.content.style = ''; self.modalParts.frame.style = ''; self.modalParts.wrapper.style = ''; self.modalParts.viewport.classList.remove('active'); setTimeout(() => { self.modalParts.class.forEach(c => { self.modalParts.viewport.classList.remove(c); }); }, 250); self.modalState = false; resolve(); }); }).then(() => { document.dispatchEvent(self.events.afterModalClose); }); }; /** * モーダルウィンドウの拡縮 * @param {float} aftScale 操作後の倍率 */ modalResize(aftScale) { const self = this; if (!self.modalState) return; let duration = self.option.modal.resizeDuration; let frame = self.option.timeFrame; let cnt = 0; let timer = null; let easing = self.getEasing(self.option.modal.resizeEasing); let befScale = self.modalParts.scale; let befWidth = self.modalParts.size.width * befScale; let befHeight = self.modalParts.size.height * befS