variable
function
isTouchDevice
タッチデバイスかどうかを判定します。
Parameters
Name Type DescriptionSource code
export const isTouchDevice =
typeof window !== 'undefined' && window.ontouchstart !== null
cssVal
HTMLタグに設置されたCSSの値を取得します。
Parameters
Name Type Descriptionproperty String CSSプロパティ
Source code
export const cssVal = (property) => {
return getComputedStyle(document.querySelector('html')).getPropertyValue(
property
)
}
$
DOM要素を返します。
Parameters
Name Type Descriptionselector String セレクタ
el HTMLElement 親要素
Example Usage
const element = $(".selector");
Source code
export const $ = (selector, el) => {
if (!el) el = document;
if (selector instanceof HTMLElement || selector instanceof SVGElement) {
el = selector;
} else {
el = el.querySelector(selector);
}
if (el === null) return null;
el.rect = (rectString) => {
switch (rectString) {
case 'x':
return el.getBoundingClientRect().x;
case 'y':
return el.getBoundingClientRect().y;
case 'left':
return el.getBoundingClientRect().x;
case 'top':
return el.getBoundingClientRect().y;
case 'width':
return el.getBoundingClientRect().width;
case 'height':
return el.getBoundingClientRect().height;
default:
throw new Error(`Invalid property: ${rectString}`);
}
};
return el;
};
$$
DOM要素を配列にして返します。
Parameters
Name Type Descriptionselector String セレクタ
el HTMLElement 親要素
Example Usage
const selector = $$(".selector");
Source code
export const $$ = (selector, el) => {
if (!el) el = document
return Array.from(el.querySelectorAll(selector)).map((el) => $(el))
}
randomId
ランダムなIDを生成します。
Parameters
Name Type DescriptionSource code
export const randomId = () => {
const LENGTH = 4
const SOURCE = 'abcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
for (let i = 0; i < LENGTH; i++) {
result += SOURCE[Math.floor(Math.random() * SOURCE.length)]
}
return result
}
spOnly
SPサイズの時のみtrueを返します。
Parameters
Name Type DescriptionExample Usage
if (spOnly) {
console.log("SPサイズの時のみ実行される");
}
Source code
export const spOnly =
typeof window !== 'undefined' &&
window.matchMedia('(max-width: 751px)').matches
getDeviceType
デバイスタイプを取得します。
Parameters
Name Type DescriptionExample Usage
const deviceType = getDeviceType();
if (deviceType === "sp") {
console.log("SPサイズの時のみ実行される");
} else if (deviceType === "tb") {
console.log("タブレットサイズの時のみ実行される");
} else if (deviceType === "pc") {
console.log("PCサイズの時のみ実行される");
}
Source code
export const getDeviceType = () => {
if (typeof window !== 'undefined') {
if (window.matchMedia('(max-width: 751px)').matches) {
return 'sp'
} else if (window.matchMedia('(max-width: 992px)').matches) {
return 'tb'
} else {
return 'pc'
}
}
}
touchOnly
タッチデバイスであれば true を返します。
Parameters
Name Type DescriptionSource code
export const touchOnly = () => {
const ua = navigator.userAgent.toLowerCase()
return (
/android|ipod|ipad|iphone|macintosh/.test(ua) && 'ontouchend' in document
)
}
OneLineSplit
文字列を一行ごとに分割し、spanタグに格納します。
Parameters
Name Type Descriptionobj Object
obj.target String 分割対象の要素
obj.className String 分割された単語に設定するクラス
Example Usage
const split = new OneLineSplit({
target: ".split-text",
className: "split-text",
});
split.init(); // 分割
split.kill(); // 元に戻す
Source code
export class OneLineSplit {
constructor(obj) {
this.obj = obj
this.init()
const target = this.obj.target
let element = null
if (typeof target === 'object') {
element = target
} else if (typeof target === 'string') {
element = document.querySelector(target)
}
this.lines = element.querySelectorAll('.split-text')
}
init() {
const target = this.obj.target
const className = this.obj.className || 'split-text'
let element = null
if (typeof target === 'object') {
element = target
} else if (typeof target === 'string') {
element = document.querySelector(target)
}
if (element === null) return
this.obj.element = element
this.obj.old = element.innerHTML
this.obj.element
.querySelectorAll(
`br[${window.innerWidth < 750 ? 'pc-only' : 'sp-only'}]`
)
.forEach((item) => {
item.remove()
})
element.style.visibility = 'hidden'
let spanWrapText = ''
const nodes = [...element.childNodes]
nodes.forEach((node) => {
if (node.nodeType === 3) {
const text = node.textContent.replace(/\r?\n/g, '\n')
spanWrapText =
spanWrapText +
text.split('').reduce((acc, v) => {
return acc + `<span>${v}</span>`
}, '')
} else if (node.nodeType === 1) {
const tag = node.tagName.toLowerCase()
if (tag !== 'br') {
const attributes = node.attributes
let attr = ''
for (let i = 0; i < attributes.length; i++) {
attr += `${attributes[i].name}="${attributes[i].value}" `
}
spanWrapText =
spanWrapText +
`<${tag} ${attr} char-ignore>${node.innerHTML}</${tag}>`
} else {
spanWrapText = spanWrapText + `<br>`
}
}
})
element.innerHTML = spanWrapText
const spanEls = [...element.childNodes].filter((node) => {
return node.nodeType === 1 && node.tagName.toLowerCase() !== 'br'
})
let tagList = []
spanEls.forEach((el) => {
const top = Math.round(el.getBoundingClientRect().top)
el.setAttribute('char-pos', top)
tagList.push(top)
})
tagList = tagList.filter(function (x, i, self) {
return self.indexOf(x) === i
})
const textArray = []
tagList.forEach((item) => {
const classNameArray = document.querySelectorAll(`[char-pos="${item}"]`)
let text = ''
classNameArray.forEach((el) => {
text += el.outerHTML
})
textArray.push(text)
})
let newSpanEl = ''
textArray.forEach((text) => {
if (text === '') return
newSpanEl += `<span class="${className}" style="white-space: nowrap; display: inline-block; will-change: transform">${text}</span><br/>`
})
element.innerHTML = newSpanEl
$$(`.${className}`, element).forEach((el) => {
const array = [...$$('*', el)]
const text = array
.map((char) => {
if (char.hasAttribute('char-ignore')) {
char.removeAttribute('char-ignore')
char.removeAttribute('char-pos')
return char.outerHTML
}
return char.textContent
})
.join('')
// el.innerHTML = `<span>${text}</span>`;
el.innerHTML = text
})
element.style.visibility = ''
}
kill() {
this.obj.element.innerHTML = this.obj.old
}
}
WordsSplit
文章を単語ごとに分割します。
Parameters
Name Type Descriptionobj Object
obj.target String 分割対象の要素
obj.tag String 分割された単語に設定するタグ
Example Usage
const wordsSplit = new WordsSplit({
target: document.getElementById("target"),
tag: "span",
});
wordsSplit.init();
const words = wordsSplit.kill();
Source code
export class WordsSplit {
constructor(obj){
// example obj
// {
// target: Element || String,
// tag: String,
// }
this.obj = obj
this.splitWordsSetting = /(<[^>]+>)|[\p{sc=Han}]+|[\p{sc=Katakana}ー]+|[\p{sc=Hiragana}]+|[\p{P}]+|\b[\w'-]+\b|[\s]+/gu;
this.rawText = this.obj.target.innerHTML
this.obj.tag = this.obj.tag || "span"
}
init(){
this.splitText = this.rawText.match(this.splitWordsSetting);
const wordsSplitArray = this.splitText.map((text,index)=>{
if(text.indexOf("<") === 0) return text
if(text === " ") return " "
return `<${this.obj.tag} style='display: inline-block'>${text}</${this.obj.tag}>`
})
this.obj.target.innerHTML = wordsSplitArray.join("");
this.words = this.obj.target.querySelectorAll(this.obj.tag)
}
kill(){
this.obj.target.innerHTML = this.rawText
}
}
getPrime
素数を返します。
Parameters
Name Type Descriptionn Number 素数の個数
Source code
export const getPrime = (n) => {
let count = 0
let num = 1
while (count < n) {
num++
let isPrime = true
for (let i = 2; i < num; i++) {
if (num % i === 0) {
isPrime = false
break
}
}
if (isPrime) {
count++
}
}
return num
}
SwipeTracker
指定したHTML要素上でのタッチ操作を監視し、ユーザーが行うスワイプの方向(左右または上下)に応じたカスタムイベントを発火させるためのものです。
Parameters
Name Type Descriptionelem HTMLElement タッチ操作を監視するHTML要素
direction String スワイプの方向('lr'または'ud')
Example Usage
const elem = document.querySelector(".swipe");
SwipeTracker(elem, "lr");
elem.addEventListener("swipe.left", () => {
console.log("swipe left");
});
elem.addEventListener("swipe.right", () => {
console.log("swipe right");
});
elem.addEventListener("swipe.cancel", () => {
console.log("swipe cancel");
});
elem.addEventListener("swipe.start", () => {
console.log("swipe start");
});
elem.addEventListener("swipe.move", () => {
console.log("swipe move");
});
elem.addEventListener("swipe.up", () => {
console.log("swipe up");
});
elem.addEventListener("swipe.down", () => {
console.log("swipe down");
});
Source code
export const SwipeTracker = (elem, direction = '') => {
let x = 0
let y = 0
let target = null
let startX = 0
let startY = 0
let moveX = 0
let moveY = 0
let thresholdX
let thresholdY
let eventList = {}
// 指定されたエレメントを検出対象にする
target = elem
// ----- 諸々関数
// スワイプと見なす閾値の設定、数値は用途で調整
const setThreshold = function () {
// 画面幅1/4または300の小さい方、割としっかり動かさないとスワイプにならない
thresholdX = Math.min(window.innerWidth / 4, 300)
// 画面高さ1/6または50の小さい方、縦は敏感
thresholdY = Math.min(window.innerHeight / 6, 50)
}
// イベント発行。同じ名前のイベントを繰り返し作るのが気にならないなら不要
const kickEvent = function (eventName) {
if (eventList[eventName] == undefined) {
eventList[eventName] = new Event(eventName)
}
target.dispatchEvent(eventList[eventName])
}
// 検出のリセット
const resetSwipe = function () {
x = 0
y = 0
startX = 0
startY = 0
moveX = 0
moveY = 0
}
// スワイプのキャンセル
const cencelSwipe = function () {
resetSwipe()
kickEvent('swipe.cancel')
}
// // 外部に移動量を教えるメソッド
// this.posx = function () {
// return x
// }
// this.posy = function () {
// return y
// }
// ---- タッチイベントを監視
// 開始
target.addEventListener(
'touchstart',
(ev) => {
// 開始座標記録
startX = ev.touches[0].pageX
startY = ev.touches[0].pageY
moveX = startX
moveY = startY
kickEvent('swipe.start')
},
{ passive: true }
)
// キャンセル
target.addEventListener('touchcancel', cencelSwipe, { passive: true })
// 移動
target.addEventListener(
'touchmove',
(ev) => {
moveX = ev.touches[0].pageX
moveY = ev.touches[0].pageY
// タッチ開始時からの差分
x = moveX - startX
y = moveY - startY
kickEvent('swipe.move')
},
{ passive: true }
)
// 終了
target.addEventListener(
'touchend',
(ev) => {
// 横方向の移動が閾値を超えてる=>左右スワイプ
if (Math.abs(x) >= thresholdX) {
if (x < 0) {
kickEvent('swipe.left')
} else {
kickEvent('swipe.right')
}
// 左右の検出をしたかったけど閾値超えず=>キャンセル
} else if (direction === 'lr') {
kickEvent('swipe.cancel')
}
// 縦方向の移動が閾値を超えてる=>上下スワイプ
if (Math.abs(y) >= thresholdY) {
if (y < 0) {
kickEvent('swipe.up')
} else {
kickEvent('swipe.down')
}
}
// 上下の検出をしたかったけど閾値超えず=>キャンセル
else if (direction === 'ud') {
kickEvent('swipe.cancel')
}
// 何にせよ値リセット
resetSwipe()
},
{ passive: true }
)
// 呼ばれた時に、閾値設定を実行
setThreshold()
// 画面リサイズ時にも再実行
window.addEventListener('resize', setThreshold)
}
shuffleArray
配列をシャッフルします。
Parameters
Name Type Descriptionarray Array 配列
Source code
export const shuffleArray = (array) => {
const cloneArray = [...array]
for (let i = cloneArray.length - 1; i >= 0; i--) {
let rand = Math.floor(Math.random() * (i + 1))
// 配列の要素の順番を入れ替える
let tmpStorage = cloneArray[i]
cloneArray[i] = cloneArray[rand]
cloneArray[rand] = tmpStorage
}
return cloneArray
}
scrollSet
'stop' か 'start' の引数を渡すとスクロールを制御できます。
Parameters
Name Type Descriptiontoggle String 'stop' or 'start'
BodyScrollLocks Boolean true or false
Example Usage
$(".btn").addEventListener("click", () => {
if ($(".menu").hasAttribute("open")) {
scrollSet("start");
} else {
scrollSet("stop");
}
});
Source code
export const scrollSet = (toggle, BodyScrollLocks) => {
if (toggle === 'stop') {
if (BodyScrollLocks) {
disableBodyScroll($('.base-grad'))
return
}
if (window.lenis) window.lenis.stop()
document.querySelector('body').style.overflow = 'hidden'
}
if (toggle === 'start') {
if (BodyScrollLocks) {
clearAllBodyScrollLocks()
return
}
if (window.lenis) window.lenis.start()
document.querySelector('body').style.overflow = ''
}
}
hoverRemoval
ホバー時に「hover-el」という属性をbuttonタグとaタグに追加します。
Parameters
Name Type DescriptionExample Usage
class App {
constructor() {
hoverRemoval();
}
}
Source code
export const hoverRemoval = () => {
document.querySelectorAll("button,a").forEach((el) => {
el.addEventListener("touchstart", () => {
el.setAttribute("hover-el", "")
})
el.addEventListener("touchend", () => {
el.removeAttribute("hover-el")
})
})
}
loopCount
数値型の引数から配列を生成します。
Parameters
Name Type Descriptioncount Number 抽出する個数
Source code
export const loopCount = (count) => {
return [...Array(count).keys()]
}
getQueryParams
URLパラメーターからクエリーを取得します。
Parameters
Name Type DescriptionSource code
export const getQueryParams = () => {
const params = new URLSearchParams(window.location.search);
const queryParams = {};
for (const [key, value] of params.entries()) {
if (value.includes(',')) {
queryParams[key] = value.split(',').map(Number); // 数字に変換する場合
} else {
queryParams[key] = value;
}
}
return queryParams;
}
DebugGrid
デザインのベースとなるグリッドを表示します。
Parameters
Name Type DescriptionExample Usage
<>
<script>
import {DebugGrid} from '@walkal0ne/lasagna'; // new
DebugGrid();とかしなくても呼び出せます。
</script>
<template lang="html">
<debug-grid></debug-grid>
</template>
</>;
Source code
const isBrowser = typeof window !== 'undefined' && typeof HTMLElement !== 'undefined';
let DebugGrid;
if (isBrowser) {
DebugGrid = class DebugGrid extends HTMLElement {
DebugImage
デザインのベースとなる画像を表示します。
Parameters
Name Type DescriptionExample Usage
<>
<script>
import {DebugImage} from '@walkal0ne/lasagna'; // new
DebugImage();とかしなくても呼び出せます。
</script>
<template lang="html">
<debug-image pc-image="pc.jpg" sp-image="sp.jpg"></debug-image>
</template>
</>;
Source code
let DebugImage;
if (isBrowser) {
DebugImage = class DebugImage extends HTMLElement{
// コンストラクタでシャドウDOMを作成
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
// 要素がDOMに接続されたときの処理
connectedCallback() {
this.render();
this.initToggle();
this.listenKey();
}
// 要素がDOMから切り離されたときはイベントリスナーを解除
disconnectedCallback() {
document.removeEventListener('keydown', this.handleKeydown);
}
// シャドウDOM内にHTMLとスタイルを構築する
render() {
const template = document.createElement('template');
template.innerHTML = `
<style>
:host {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
margin: auto;
opacity: 0.5;
z-index: 9999;
width: 100vw;
height: fit-content;
display: none;
}
:host([debug-show]) {
display: block;
}
.debug-image{
width: 100%;
height: auto;
source, img {
width: 100%;
height: auto;
}
}
</style>
<picture class="debug-image">
<source media="(min-width: 751px)" srcset="${this.getAttribute('pc-image')}">
<source media="(max-width: 750px)" srcset="${this.getAttribute('sp-image')}">
<img src="${this.getAttribute('pc-image')}" alt="表示する画像">
</picture>
`;
this.shadowRoot.appendChild(template.content.cloneNode(true));
const gridContainer = this.shadowRoot.querySelector('.debug-image');
// this.addImageClickListener(gridContainer);
}
// セッションストレージから状態を読み込み、表示を切り替え
initToggle() {
const toggle = sessionStorage.getItem('debugImageToggle');
if (toggle !== null) {
if (toggle === 'true') {
this.setAttribute('debug-show', '');
} else {
this.removeAttribute('debug-show');
}
}
}
// 「P」キーでグリッドの表示/非表示を切り替える(大文字のみ対応)
listenKey() {
this.handleKeydown = (e) => {
if (e.key === 'P') {
if (this.hasAttribute('debug-show')) {
this.removeAttribute('debug-show');
} else {
this.setAttribute('debug-show', '');
}
sessionStorage.setItem('debugImageToggle', String(this.hasAttribute('debug-show')));
}
};
document.addEventListener('keydown', this.handleKeydown, false);
}
}
// すでに登録されていなければカスタム要素として定義
if (!customElements.get('debug-image')) {
customElements.define('debug-image', DebugImage);
}
}else{
DebugImage = class {};
}
export { DebugImage };
safeCall
関数を安全に呼び出します。
Parameters
Name Type Descriptionfn Function 呼び出す関数
Example Usage
import { safeCall } from "@walkal0ne/lasagna";
export default class App {
init() {
if ($("[page-name='index']") === null) return;
safeCall(this.opening.bind(this));
safeCall(this.indexDirection.bind(this));
safeCall(this.aboutDirection.bind(this));
safeCall(this.globalDirection.bind(this));
}
}
// or
[
this.opening,
this.indexDirection,
this.aboutDirection,
this.globalDirection,
].forEach((fn) => safeCall(fn.bind(this)));
Source code
export function safeCall(fn) {
try {
fn();
} catch (e) {
const name = fn.name || 'anonymous';
console.error(`${name} error:`, e);
}
}