mirror of
https://iceshrimp.dev/Crimekillz/jointrashposs.git
synced 2024-11-25 02:09:05 +01:00
feat(tools): 初期アイコンジェネレーター (#131)
This commit is contained in:
parent
5d188439cb
commit
ee1e3cf773
@ -24,6 +24,11 @@ export default <NavSection[]>[
|
||||
description: "_shareLinkGenerator.description",
|
||||
to: "/tools/share-link-generator/",
|
||||
},
|
||||
{
|
||||
i18n: '_identiconGenerator.title',
|
||||
description: '_identiconGenerator.description',
|
||||
to: '/tools/identicon-generator/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
104
assets/js/mi/gen-identicon.ts
Normal file
104
assets/js/mi/gen-identicon.ts
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* Identicon generator
|
||||
* https://en.wikipedia.org/wiki/Identicon
|
||||
*/
|
||||
|
||||
import gen from 'random-seed';
|
||||
|
||||
const size = 128; // px
|
||||
const n = 5; // resolution
|
||||
const margin = (size / 4);
|
||||
const colors = [
|
||||
['#FF512F', '#DD2476'],
|
||||
['#FF61D2', '#FE9090'],
|
||||
['#72FFB6', '#10D164'],
|
||||
['#FD8451', '#FFBD6F'],
|
||||
['#305170', '#6DFC6B'],
|
||||
['#00C0FF', '#4218B8'],
|
||||
['#009245', '#FCEE21'],
|
||||
['#0100EC', '#FB36F4'],
|
||||
['#FDABDD', '#374A5A'],
|
||||
['#38A2D7', '#561139'],
|
||||
['#121C84', '#8278DA'],
|
||||
['#5761B2', '#1FC5A8'],
|
||||
['#FFDB01', '#0E197D'],
|
||||
['#FF3E9D', '#0E1F40'],
|
||||
['#766eff', '#00d4ff'],
|
||||
['#9bff6e', '#00d4ff'],
|
||||
['#ff6e94', '#00d4ff'],
|
||||
['#ffa96e', '#00d4ff'],
|
||||
['#ffa96e', '#ff009d'],
|
||||
['#ffdd6e', '#ff009d'],
|
||||
];
|
||||
|
||||
const actualSize = size - (margin * 2);
|
||||
const cellSize = actualSize / n;
|
||||
const sideN = Math.floor(n / 2);
|
||||
|
||||
/**
|
||||
* canvasにIdenticonを描画する
|
||||
* @param seed アカウントのacct
|
||||
*/
|
||||
export function genIdenticon(seed: string, canvas: HTMLCanvasElement) {
|
||||
const rand = gen.create(seed);
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
if (!ctx) return;
|
||||
|
||||
const bgColors = colors[rand(colors.length)];
|
||||
|
||||
const bg = ctx.createLinearGradient(0, 0, size, size);
|
||||
bg.addColorStop(0, bgColors[0]);
|
||||
bg.addColorStop(1, bgColors[1]);
|
||||
|
||||
ctx.fillStyle = bg as any;
|
||||
ctx.beginPath();
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
|
||||
ctx.fillStyle = '#ffffff';
|
||||
|
||||
// side bitmap (filled by false)
|
||||
const side: boolean[][] = new Array(sideN);
|
||||
for (let i = 0; i < side.length; i++) {
|
||||
side[i] = new Array(n).fill(false);
|
||||
}
|
||||
|
||||
// 1*n (filled by false)
|
||||
const center: boolean[] = new Array(n).fill(false);
|
||||
|
||||
for (let x = 0; x < side.length; x++) {
|
||||
for (let y = 0; y < side[x].length; y++) {
|
||||
side[x][y] = rand(3) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < center.length; i++) {
|
||||
center[i] = rand(3) === 0;
|
||||
}
|
||||
|
||||
// Draw
|
||||
for (let x = 0; x < n; x++) {
|
||||
for (let y = 0; y < n; y++) {
|
||||
const isXCenter = x === ((n - 1) / 2);
|
||||
if (isXCenter && !center[y]) continue;
|
||||
|
||||
const isLeftSide = x < ((n - 1) / 2);
|
||||
if (isLeftSide && !side[x][y]) continue;
|
||||
|
||||
const isRightSide = x > ((n - 1) / 2);
|
||||
if (isRightSide && !side[sideN - (x - sideN)][y]) continue;
|
||||
|
||||
const actualX = margin + (cellSize * x);
|
||||
const actualY = margin + (cellSize * y);
|
||||
ctx.beginPath();
|
||||
ctx.fillRect(actualX, actualY, cellSize, cellSize);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@ share: "共有する"
|
||||
note: "ノート"
|
||||
other: "その他"
|
||||
add: "追加"
|
||||
generate: "生成"
|
||||
download: "ダウンロード"
|
||||
browse: "参照"
|
||||
settings: "設定"
|
||||
goToLegacyHub: "従来のMisskey Hub"
|
||||
@ -336,6 +338,12 @@ _customEmojiPreview:
|
||||
_placeholder:
|
||||
noteText: "カスタム絵文字はこんな感じで表示されます→ :emoji_preview_1:\n文章を書き換えて、使い勝手を試してみてくださいね✨"
|
||||
|
||||
_identiconGenerator:
|
||||
title: "初期アイコンジェネレーター"
|
||||
description: "Misskeyに登録した際にデフォルトで指定される初期アイコンを生成できます。"
|
||||
userName: "ユーザー名"
|
||||
includeDomain: "サーバーのドメイン名を含む完全なユーザー名を指定してください。"
|
||||
|
||||
_api:
|
||||
_permissions:
|
||||
title: "権限"
|
||||
|
@ -17,6 +17,7 @@
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^20.11.28",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/random-seed": "^0.3.5",
|
||||
"@types/ua-parser-js": "^0.7.39",
|
||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.5",
|
||||
"autoprefixer": "^10.4.18",
|
||||
@ -29,6 +30,7 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"nuxt": "^3.11.0",
|
||||
"postcss": "^8.4.36",
|
||||
"random-seed": "^0.3.0",
|
||||
"sass": "^1.72.0",
|
||||
"schema-dts": "^1.1.2",
|
||||
"sitemap": "^7.1.1",
|
||||
|
87
pages/tools/identicon-generator.vue
Normal file
87
pages/tools/identicon-generator.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class='container mx-auto max-w-screen-xl px-6 py-6'>
|
||||
<h1 class='text-2xl lg:text-3xl font-bold mb-4'>
|
||||
{{ $t('_identiconGenerator.title') }}
|
||||
</h1>
|
||||
<div class="mx-auto max-w-lg">
|
||||
<label class="mb-1" for="acct">{{ $t('_identiconGenerator.userName') }}</label>
|
||||
<input class="form-control" id="acct" v-model="acct" placeholder="@ai@misskey.example.com" />
|
||||
<div class="form-text">{{ $t('_identiconGenerator.includeDomain') }}</div>
|
||||
<div class="mt-2 mb-4 text-center">
|
||||
<button class="btn btn-primary" @click="genIdenticon()">{{ $t('generate') }}</button>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<CaretDownFillIco class="w-12 h-12 mx-auto" />
|
||||
</div>
|
||||
<div class="mb-2 p-4 rounded-lg border bg-white dark:bg-slate-950 border-slate-300 dark:border-slate-800">
|
||||
<canvas ref="canvas" width="128" height="128" class="w-full max-w-40 h-auto mx-auto mb-4 rounded-full bg-slate-200 dark:bg-slate-700" />
|
||||
<div class="text-center">
|
||||
<button class="btn btn-primary" @click="download()" :disabled="!onceGenerated">{{ $t('download') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { genIdenticon as _genIdenticon } from '@/assets/js/mi/gen-identicon';
|
||||
import CaretDownFillIco from 'bi/caret-down-fill.svg';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'tools',
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const onceGenerated = ref(false);
|
||||
const acct = ref('@ai@misskey.example.com');
|
||||
const normalizedAcct = computed(() => {
|
||||
const normalized = acct.value.replace(/^@/, '');
|
||||
if (normalized.includes('@')) {
|
||||
return normalized;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||
|
||||
function genIdenticon() {
|
||||
if (!process.client || !canvas.value) return;
|
||||
if (!normalizedAcct.value) {
|
||||
onceGenerated.value = false;
|
||||
const ctx = canvas.value.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_genIdenticon(normalizedAcct.value, canvas.value);
|
||||
onceGenerated.value = true;
|
||||
}
|
||||
|
||||
function download() {
|
||||
if (!process.client || !normalizedAcct.value || !canvas.value) return;
|
||||
const url = canvas.value.toDataURL('image/png');
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'identicon.png';
|
||||
a.click();
|
||||
a.remove();
|
||||
}
|
||||
|
||||
route.meta.title = t('_aidConverter.title');
|
||||
route.meta.description = t('_aidConverter.description');
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
.mfmRoot {
|
||||
@apply rounded-lg p-6 border break-words overflow-hidden;
|
||||
font-family: Hiragino Maru Gothic Pro,BIZ UDGothic,Roboto,HelveticaNeue,Arial,sans-serif;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.mfmRoot img {
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
@ -34,6 +34,9 @@ devDependencies:
|
||||
'@types/nprogress':
|
||||
specifier: ^0.2.3
|
||||
version: 0.2.3
|
||||
'@types/random-seed':
|
||||
specifier: ^0.3.5
|
||||
version: 0.3.5
|
||||
'@types/ua-parser-js':
|
||||
specifier: ^0.7.39
|
||||
version: 0.7.39
|
||||
@ -70,6 +73,9 @@ devDependencies:
|
||||
postcss:
|
||||
specifier: ^8.4.36
|
||||
version: 8.4.36
|
||||
random-seed:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0
|
||||
sass:
|
||||
specifier: ^1.72.0
|
||||
version: 1.72.0
|
||||
@ -2294,6 +2300,10 @@ packages:
|
||||
resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
|
||||
dev: true
|
||||
|
||||
/@types/random-seed@0.3.5:
|
||||
resolution: {integrity: sha512-CftxcDPAHgs0SLHU2dt+ZlDPJfGqLW3sZlC/ATr5vJDSe5tRLeOne7HMvCOJnFyF8e1U41wqzs3h6AMC613xtA==}
|
||||
dev: true
|
||||
|
||||
/@types/resolve@1.20.2:
|
||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||
dev: true
|
||||
@ -4849,6 +4859,10 @@ packages:
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
dev: true
|
||||
|
||||
/json-stringify-safe@5.0.1:
|
||||
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
|
||||
dev: true
|
||||
|
||||
/json5@2.2.3:
|
||||
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
||||
engines: {node: '>=6'}
|
||||
@ -6854,6 +6868,13 @@ packages:
|
||||
resolution: {integrity: sha512-yUUd5VTiFtcMEx0qFUxGAv5gbMc1un4RvEO1JZdP7ZUl/RHygZK6PknIKntmQRZxnMY3ZXD2ISaw1ij8GYW1yg==}
|
||||
dev: true
|
||||
|
||||
/random-seed@0.3.0:
|
||||
resolution: {integrity: sha512-y13xtn3kcTlLub3HKWXxJNeC2qK4mB59evwZ5EkeRlolx+Bp2ztF7LbcZmyCnOqlHQrLnfuNbi1sVmm9lPDlDA==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
dependencies:
|
||||
json-stringify-safe: 5.0.1
|
||||
dev: true
|
||||
|
||||
/randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user