This commit is contained in:
Ayaka
2022-10-11 13:35:43 +00:00
commit 47a2ca33b8
92 changed files with 31009 additions and 0 deletions

68
src/App.css Normal file
View File

@@ -0,0 +1,68 @@
.App {
text-align: center;
}
.container {
padding: 4rem;
align-items: center;
justify-content: center;
display: flex;
flex-direction: column;
}
.canvas {
height: 256px;
width: 296px;
border: 1px solid #ddd;
}
.vertical {
display: flex;
justify-content: center;
align-items: center;
height: 256px;
}
.horizontal {
width: 296px;
margin-left: -30px;
}
.settings {
color: white;
font-family: "YurukaStd";
margin: 1.5rem 0 1.5rem 0;
}
.settings > div {
display: flex;
align-items: center;
gap: 1.5rem;
margin-bottom: 1rem;
}
.text {
margin-bottom: 1rem;
}
.picker {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.footer {
margin-top: 2rem;
font-family: "YurukaStd";
}
.footer > a {
color: rgb(199, 199, 199);
text-decoration: none;
}
.footer > a:hover {
color: rgb(255, 255, 255);
}

151
src/App.jsx Normal file
View File

@@ -0,0 +1,151 @@
import "./App.css";
import Canvas from "./components/Canvas";
import { useState, useEffect } from "react";
import characters from "./characters.json";
import Slider from "@mui/material/Slider";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import Picker from "./components/Picker";
function App() {
const [character, setCharacter] = useState(49);
const [text, setText] = useState(characters[character].defaultText.text);
const [position, setPosition] = useState({
x: characters[character].defaultText.x,
y: characters[character].defaultText.y,
});
const [fontSize, setFontSize] = useState(characters[character].defaultText.s);
const [rotate, setRotate] = useState(characters[character].defaultText.r);
const [loaded, setLoaded] = useState(false);
const img = new Image();
useEffect(() => {
setText(characters[character].defaultText.text);
setPosition({
x: characters[character].defaultText.x,
y: characters[character].defaultText.y,
});
setRotate(characters[character].defaultText.r);
setFontSize(characters[character].defaultText.s);
setLoaded(false);
}, [character]);
img.src = "/img/" + characters[character].img;
img.onload = () => {
setLoaded(true);
};
const draw = (ctx) => {
ctx.canvas.width = 296;
ctx.canvas.height = 256;
if (loaded && document.fonts.check("12px YurukaStd")) {
ctx.drawImage(img, 0, 0, 296, 256);
ctx.font = `${fontSize}px YurukaStd`;
ctx.lineWidth = 9;
ctx.save();
ctx.translate(position.x, position.y);
ctx.rotate(rotate / 10);
ctx.textAlign = "center";
ctx.strokeStyle = "white";
ctx.strokeText(text, 0, 0);
ctx.fillStyle = characters[character].color;
ctx.fillText(text, 0, 0);
ctx.restore();
}
};
const download = () => {
const canvas = document.getElementsByTagName("canvas")[0];
const link = document.createElement("a");
link.download = `${characters[character].name}_st.ayaka.one.png`;
link.href = canvas.toDataURL();
link.click();
};
return (
<div className="App">
<div className="container">
<div className="vertical">
<div className="canvas">
<Canvas draw={draw} />
</div>
<Slider
value={256 - position.y}
onChange={(e, v) => setPosition({ ...position, y: 256 - v })}
min={0}
max={256}
step={1}
orientation="vertical"
track={false}
color="secondary"
/>
</div>
<div className="horizontal">
<Slider
value={position.x}
onChange={(e, v) => setPosition({ ...position, x: v })}
min={0}
max={296}
step={1}
track={false}
color="secondary"
/>
<div className="settings">
<div>
<label>Rotate: </label>
<Slider
value={rotate}
onChange={(e, v) => setRotate(v)}
min={-10}
max={10}
step={1}
track={false}
color="secondary"
/>
</div>
<div>
<label>
<nobr>Font size: </nobr>
</label>
<Slider
value={fontSize}
onChange={(e, v) => setFontSize(v)}
min={10}
max={100}
step={1}
track={false}
color="secondary"
/>
</div>
</div>
<div className="text">
<TextField
label="Text"
size="small"
color="secondary"
value={text}
fullWidth
onChange={(e) => setText(e.target.value)}
/>
</div>
<div className="picker">
<Picker setCharacter={setCharacter} />
<Button color="secondary" onClick={download}>
download
</Button>
</div>
</div>
<div className="footer">
<a href="https://github.com/theoriginalayaka/sekai-stickers">
GitHub
</a>
</div>
</div>
</div>
);
}
export default App;

913
src/characters.json Normal file
View File

@@ -0,0 +1,913 @@
[
{
"id": "3",
"name": "Airi 01",
"character": "airi",
"img": "airi/Airi_01.png",
"color": "#FB8AAC",
"defaultText": {
"text": "keep up",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "4",
"name": "Airi 02",
"character": "airi",
"img": "airi/Airi_02.png",
"color": "#FB8AAC",
"defaultText": {
"text": "nice to meet ya",
"x": 148,
"y": 58,
"r": 0,
"s": 28
}
},
{
"id": "5",
"name": "Airi 03",
"character": "airi",
"img": "airi/Airi_03.png",
"color": "#FB8AAC",
"defaultText": {
"text": "keep at it!",
"x": 140,
"y": 79,
"r": 2,
"s": 47
}
},
{
"id": "6",
"name": "Airi 04",
"character": "airi",
"img": "airi/Airi_04.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "8",
"name": "Airi 06",
"character": "airi",
"img": "airi/Airi_06.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "9",
"name": "Airi 07",
"character": "airi",
"img": "airi/Airi_07.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "10",
"name": "Airi 08",
"character": "airi",
"img": "airi/Airi_08.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "11",
"name": "Airi 09",
"character": "airi",
"img": "airi/Airi_09.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "13",
"name": "Airi 11",
"character": "airi",
"img": "airi/Airi_11.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "14",
"name": "Airi 12",
"character": "airi",
"img": "airi/Airi_12.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "15",
"name": "Airi 13",
"character": "airi",
"img": "airi/Airi_13.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "16",
"name": "Airi 14",
"character": "airi",
"img": "airi/Airi_14.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "18",
"name": "Airi 16",
"character": "airi",
"img": "airi/Airi_16.png",
"color": "#FB8AAC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "19",
"name": "Akito 01",
"character": "akito",
"img": "akito/Akito_01.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "20",
"name": "Akito 02",
"character": "akito",
"img": "akito/Akito_02.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "21",
"name": "Akito 03",
"character": "akito",
"img": "akito/Akito_03.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "22",
"name": "Akito 04",
"character": "akito",
"img": "akito/Akito_04.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "24",
"name": "Akito 06",
"character": "akito",
"img": "akito/Akito_06.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "25",
"name": "Akito 07",
"character": "akito",
"img": "akito/Akito_07.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "26",
"name": "Akito 08",
"character": "akito",
"img": "akito/Akito_08.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "27",
"name": "Akito 09",
"character": "akito",
"img": "akito/Akito_09.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "29",
"name": "Akito 11",
"character": "akito",
"img": "akito/Akito_11.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "30",
"name": "Akito 12",
"character": "akito",
"img": "akito/Akito_12.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "31",
"name": "Akito 13",
"character": "akito",
"img": "akito/Akito_13.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "32",
"name": "Akito 14",
"character": "akito",
"img": "akito/Akito_14.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "33",
"name": "Akito 16",
"character": "akito",
"img": "akito/Akito_16.png",
"color": "#FF7722",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "34",
"name": "An 01",
"character": "an",
"img": "an/An_01.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "35",
"name": "An 02",
"character": "an",
"img": "an/An_02.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "36",
"name": "An 03",
"character": "an",
"img": "an/An_03.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "37",
"name": "An 04",
"character": "an",
"img": "an/An_04.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "39",
"name": "An 06",
"character": "an",
"img": "an/An_06.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "40",
"name": "An 07",
"character": "an",
"img": "an/An_07.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "41",
"name": "An 08",
"character": "an",
"img": "an/An_08.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "42",
"name": "An 09",
"character": "an",
"img": "an/An_09.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "44",
"name": "An 11",
"character": "an",
"img": "an/An_11.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "45",
"name": "An 12",
"character": "an",
"img": "an/An_12.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "46",
"name": "An 13",
"character": "an",
"img": "an/An_13.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "47",
"name": "An 14",
"character": "an",
"img": "an/An_14.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "49",
"name": "An 16",
"character": "an",
"img": "an/An_16.png",
"color": "#00BADC",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "50",
"name": "Emu 01",
"character": "emu",
"img": "emu/Emu_01.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "51",
"name": "Emu 02",
"character": "emu",
"img": "emu/Emu_02.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "52",
"name": "Emu 03",
"character": "emu",
"img": "emu/Emu_03.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "53",
"name": "Emu 04",
"character": "emu",
"img": "emu/Emu_04.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "55",
"name": "Emu 06",
"character": "emu",
"img": "emu/Emu_06.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "56",
"name": "Emu 07",
"character": "emu",
"img": "emu/Emu_07.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "57",
"name": "Emu 08",
"character": "emu",
"img": "emu/Emu_08.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "58",
"name": "Emu 09",
"character": "emu",
"img": "emu/Emu_09.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "60",
"name": "Emu 11",
"character": "emu",
"img": "emu/Emu_11.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "61",
"name": "Emu 12",
"character": "emu",
"img": "emu/Emu_12.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "62",
"name": "Emu 13",
"character": "emu",
"img": "emu/Emu_13.png",
"color": "#FF66BB",
"defaultText": {
"text": "Wonderhoy!",
"x": 148,
"y": 70,
"r": -2,
"s": 38
}
},
{
"id": "63",
"name": "Emu 14",
"character": "emu",
"img": "emu/Emu_14.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "65",
"name": "Emu 16",
"character": "emu",
"img": "emu/Emu_16.png",
"color": "#FF66BB",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "66",
"name": "Ena 01",
"character": "ena",
"img": "ena/Ena_01.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "67",
"name": "Ena 02",
"character": "ena",
"img": "ena/Ena_02.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "68",
"name": "Ena 03",
"character": "ena",
"img": "ena/Ena_03.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "69",
"name": "Ena 04",
"character": "ena",
"img": "ena/Ena_04.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "71",
"name": "Ena 06",
"character": "ena",
"img": "ena/Ena_06.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "72",
"name": "Ena 07",
"character": "ena",
"img": "ena/Ena_07.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "73",
"name": "Ena 08",
"character": "ena",
"img": "ena/Ena_08.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "74",
"name": "Ena 09",
"character": "ena",
"img": "ena/Ena_09.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "76",
"name": "Ena 11",
"character": "ena",
"img": "ena/Ena_11.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "77",
"name": "Ena 12",
"character": "ena",
"img": "ena/Ena_12.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "78",
"name": "Ena 13",
"character": "ena",
"img": "ena/Ena_13.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "79",
"name": "Ena 14",
"character": "ena",
"img": "ena/Ena_14.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
},
{
"id": "81",
"name": "Ena 16",
"character": "ena",
"img": "ena/Ena_16.png",
"color": "#B18F6C",
"defaultText": {
"text": "something",
"x": 148,
"y": 58,
"r": -2,
"s": 47
}
}
]

21
src/components/Canvas.jsx Normal file
View File

@@ -0,0 +1,21 @@
import { useRef, useEffect } from 'react'
import "../index.css"
const Canvas = props => {
const { draw, ...rest } = props
const canvasRef = useRef(null)
useEffect(() => {
const canvas = canvasRef.current
const context = canvas.getContext('2d')
draw(context)
}, [draw])
return <canvas ref={canvasRef} {...rest}/>
}
export default Canvas

72
src/components/Picker.jsx Normal file
View File

@@ -0,0 +1,72 @@
import ImageList from "@mui/material/ImageList";
import ImageListItem from "@mui/material/ImageListItem";
import Popover from "@mui/material/Popover";
import Button from "@mui/material/Button";
import { useState } from "react";
import characters from "../characters.json";
export default function Picker({ setCharacter }) {
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "picker" : undefined;
return (
<div>
<Button
aria-describedby={id}
variant="contained"
color="secondary"
onClick={handleClick}
>
Pick character
</Button>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
>
<ImageList sx={{ width: 500, height: 450 }} cols={4} rowHeight={120}>
{characters.map((c, index) => (
<ImageListItem
key={index}
onClick={() => {
handleClose();
setCharacter(index);
}}
sx={{
cursor: "pointer",
"&:hover": {
opacity: 0.5,
},
"&:active": {
opacity: 0.8,
},
}}
>
<img
src={`/img/${c.img}`}
srcSet={`/img/${c.img}`}
alt={c.name}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</Popover>
</div>
);
}

BIN
src/fonts/YurukaStd.woff2 Normal file

Binary file not shown.

10
src/index.css Normal file
View File

@@ -0,0 +1,10 @@
body {
margin: 0;
padding: 0;
background-color: rgb(87, 73, 91);
}
@font-face {
font-family: 'YurukaStd';
src: local('YurukaStd'), url(./fonts/YurukaStd.woff2) format('woff2');
}

22
src/index.jsx Normal file
View File

@@ -0,0 +1,22 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
const darkTheme = createTheme({
palette: {
mode: "dark",
},
});
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<ThemeProvider theme={darkTheme}>
<CssBaseline />
<App />
</ThemeProvider>
</React.StrictMode>
);