自分用にCustom8 JSMacro KeyPadというお気楽ツールを作りました
ボタン毎にのJSが登録できてマクロボタン的に実行する画面です。
evalで実行してるだけです。
ElgadoのStream DECKが格好良いので真似したくてしょぼい画面を作りました。
1.ツールの画面
Stream DECKと全然違いますが…オマージュということで、JSのマクロ登録するとこんな感じです。
2.ソースコード
ソースコードというかcodepenに作ったのでそちらでソースもご確認ください。
https://codepen.io/sf-os/pen/YzoOKed
See the Pen Custom8 JSMacro KeyPad by sf-os (@sf-os) on CodePen.
※少しCSS追加したバージョンもあります。 https://codepen.io/sf-os/pen/VwJVeBj
3.登録できるマクロの例
ボタン番号選んでボタン名とJSを登録するだけです。localStoregeに保存されます。
- Hello World
alert("hello world");
- 台風情報HPを開く
var result = window.confirm('気象庁台風情報を開きますか');
if (result) {window.open('https://www.jma.go.jp/bosai/map.html#6/33.978/140.682/&elem=root&typhoon=all&contents=typhoon')};
- Yahoo NewsのRSS取得
// div要素の作成
const yahooNewsDiv = document.createElement('div');
yahooNewsDiv.id = 'yahoonews';
document.body.appendChild(yahooNewsDiv);
// RSSフィードのURL
const rssUrls = [
'https://news.yahoo.co.jp/rss/topics/top-picks.xml',
'https://news.yahoo.co.jp/rss/topics/domestic.xml',
'https://news.yahoo.co.jp/rss/topics/world.xml'
];
// RSSフィードを取得して表示する関数
async function fetchAndDisplayRss(url) {
try {
const response = await fetch(`https://api.allorigins.win/get?url=${encodeURIComponent(url)}`);
const data = await response.json();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(data.contents, 'text/xml');
const channel = xmlDoc.querySelector('channel');
const title = channel.querySelector('title').textContent;
const items = channel.querySelectorAll('item');
let html = `<h2>${title}</h2><ul>`;
items.forEach((item, index) => {
if (index < 5) {
const itemTitle = item.querySelector('title').textContent;
const itemLink = item.querySelector('link').textContent;
const itemPubDate = new Date(item.querySelector('pubDate').textContent);
html += `
<li>
<a href="${itemLink}" target="_blank">${itemTitle}</a>
<br>
<small>公開日時: ${itemPubDate.toLocaleString('ja-JP')}</small>
</li>
`;
}
});
html += '</ul>';
yahooNewsDiv.innerHTML += html;
} catch (error) {
console.error('エラーが発生しました:', error);
yahooNewsDiv.innerHTML += `<p>RSSフィードの取得に失敗しました: ${url}</p>`;
}
}
// 全てのRSSフィードを取得して表示
async function fetchAllRssFeeds() {
for (const url of rssUrls) {
await fetchAndDisplayRss(url);
// 各リクエスト間に3秒の遅延を追加
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
// スクリプトの実行
fetchAllRssFeeds();
- シンプルメモ
function createMemoModal() {
// 既存のモーダルがあれば削除
const existingModal = document.getElementById('memoModal');
if (existingModal) {
existingModal.remove();
}
// モーダルの作成
const modal = document.createElement('dialog');
modal.id = 'memoModal';
modal.style.backgroundColor = '#888';
modal.style.padding = '20px';
modal.style.borderRadius = '5px';
// タイトルの作成
const title = document.createElement('h2');
title.textContent = 'シンプル・メモ';
title.style.marginTop = '0';
title.style.marginBottom = '15px';
title.style.color = 'white';
// テキストエリアの作成
const textarea = document.createElement('textarea');
textarea.id = 'memoText';
textarea.rows = 10;
textarea.cols = 50;
textarea.style.width = '100%';
textarea.style.marginBottom = '15px';
// 保存&終了ボタンの作成
const saveButton = document.createElement('button');
saveButton.textContent = '保存&終了';
saveButton.style.padding = '5px 10px';
saveButton.onclick = function() {
const memoContent = textarea.value;
localStorage.setItem('memosave20240831-1', memoContent);
modal.close();
};
// モーダルに要素を追加
modal.appendChild(title);
modal.appendChild(textarea);
modal.appendChild(saveButton);
// bodyにモーダルを追加
document.body.appendChild(modal);
// ローカルストレージから値を取得して表示
const savedMemo = localStorage.getItem('memosave20240831-1');
if (savedMemo) {
textarea.value = savedMemo;
}
// モーダルを表示
modal.showModal();
};
createMemoModal();
- 時計
// div要素を作成
const clockDiv = document.createElement('div');
clockDiv.id = 'clock';
document.body.appendChild(clockDiv);
// 時計を更新する関数
function updateClock() {
const now = new Date();
const days = ['日', '月', '火', '水', '木', '金', '土'];
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const date = String(now.getDate()).padStart(2, '0');
const day = days[now.getDay()];
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const timeString = `${year}年${month}月${date}日 (${day}) ${hours}:${minutes}:${seconds}`;
document.getElementById('clock').textContent = timeString;
}
// 1秒ごとに時計を更新
setInterval(updateClock, 1000);
// 初回の更新を即時実行
updateClock();
- 地図
let leafletPromise = null;
function loadLeaflet() {
if (leafletPromise) {
return leafletPromise;
}
leafletPromise = new Promise((resolve, reject) => {
if (window.L) {
resolve(window.L);
return;
}
const linkElement = document.createElement('link');
linkElement.rel = 'stylesheet';
linkElement.href = 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css';
document.head.appendChild(linkElement);
const scriptElement = document.createElement('script');
scriptElement.src = 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js';
scriptElement.onload = () => resolve(window.L);
scriptElement.onerror = () => reject(new Error('Leafletスクリプトの読み込みに失敗しました。'));
document.body.appendChild(scriptElement);
});
return leafletPromise;
}
function createLeafletMap(containerId, lat, lon, zoom) {
return loadLeaflet().then(L => {
let container = document.getElementById(containerId);
if (!container) {
container = document.createElement('div');
container.id = containerId;
container.style.width = '100%';
container.style.height = '400px';
document.body.appendChild(container);
}
const map = L.map(container).setView([lat, lon], zoom);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
L.marker([lat, lon]).addTo(map)
.bindPopup('ここが指定された位置です')
.openPopup();
return map;
});
}
// 使用例
createLeafletMap('map-container-1', 35.6586, 139.7454, 15)
.then(map => console.log('地図1が作成されました'));
4.おわりに
しょぼいけど、ランチャー的に使えそうなので記事にしました。
CSS得意なひとならもっとかっこよくできそうです。
以上