リポジトリ
Web版
絵コンテはアニメ制作における設計図です。カット割り、セリフ、トランジション、カメラワーク等、アニメ制作を進める上で必要な情報がぎっしり詰まっています。従来(そして多くの場合現在も)絵コンテは紙とペンで作られてきました。
アニメ制作では各工程において、タイムシート、脚本、字幕、レイアウト等、様々な形式のデータを使用しますが、これらのデータは全て絵コンテを元に作られます。何度も再利用されるデータの管理は、デジタルの最も得意とすることろです。そこで、スタジオみずたまではアニメ制作の経験を生かし、デジタル絵コンテエディタアプリを開発することにしました。
先行アプリ
Storyboard Pro
新海誠さんも使ってるプロプライエタリアプリです。あまりにも高額で自主制作への導入はハードルが高いです。
Storyboarder
無料で使える海外のOSSです。Electron製です。高機能ですが、カメラワークやトランジションを設定できない等、痒いところに手が届かない部分があります。
e-Conte Board
iPadアプリです。日本の映像制作において必要十分な機能が揃っていますが、iPad専用のため、データの共有が難しい部分があります。
Griffith
Webベースのアプリです。一般公開はされていないようです。
Mizutama Conteで実現したいこと
いつでもどこでもプレビューできる
絵コンテはいろんな工程でいろんな立場の人が参照します。専用ソフトがなくてもブラウザだけでプレビューできるよう、json
ファイルとpsd
ファイルで絵コンテファイルを設計しました。
作画は使い慣れた外部アプリで
CLIP STUDIO PAINTやPhotoshop等、使い慣れたペイントアプリは人それぞれです。誰もが満足する描き心地を実装することは不可能であり、また、絵コンテ制作のためだけに普段と異なる環境で作画することはストレスになり得ます。そこで、このアプリでは敢えてペイント機能は実装せず、作画はPSD形式に対応した好みの外部アプリを連携させる方針にしました。
バージョン管理
コアとなる情報をjson
ファイルに記述することで、Git
によるバージョン管理が可能になります。
開発を支える技術の紹介
React Spectrum
Adobeのデザインシステム「Spectrum」のReactコンポーネントです。
基本的に既存のコンポーネントを使いましたが、足りない部分は独自にコンポーネントを作っています。
例1:アプリフレーム
const BackGround = styled.div`
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
padding: 0;
background-color: var(--spectrum-alias-appframe-border-color);
`;
const ToolArea = styled.div<{ gridArea: string }>`
background-color: var(--spectrum-alias-toolbar-background-color);
grid-area: ${({ gridArea }) => gridArea};
height: 100%;
overflow: hidden;
`;
const GlobalGrid: React.FC = ({ children }) => (
<Provider theme={defaultTheme}>
<GlobalStyle />
<BackGround>
<Grid
areas={['header header header', 'toolbar content sidebar']}
columns={['size-600', 'auto', 'size-3600']}
rows={['size-500', 'auto']}
height="100vh"
gap="size-25"
>
{children}
</Grid>
</BackGround>
</Provider>
);
例2:アコーディオンメニュー
const LabelHover = styled.div`
width: 100%;
margin: var(--spectrum-global-dimension-size-50, var(--spectrum-alias-size-50)) 0;
padding: var(--spectrum-global-dimension-size-50, var(--spectrum-alias-size-50)) 0;
:hover {
background-color: var(--spectrum-alias-highlight-hover);
border-radius: var(--spectrum-global-dimension-size-50, var(--spectrum-alias-size-50));
}
`;
const Toggle = styled.input`
display: none;
`;
const Ul = styled.ul`
padding-left: var(--spectrum-global-dimension-size-300, var(--spectrum-alias-size-300));
margin: 0;
`;
const Label: React.FC<{ labelFor: string }> = ({ labelFor, children }) => (
<LabelHover>
<label htmlFor={labelFor}>{children}</label>
</LabelHover>
);
export const Accordion: React.FC<{ labelName: string }> = ({ labelName, children }) => {
const [toggle, setToggle] = useState(false);
return (
<>
<Toggle type="checkbox" id={labelName} checked={toggle} onClick={() => setToggle(!toggle)} />
<Label labelFor={labelName}>
<Flex direction="row" gap="size-100" alignItems="center">
{toggle ? <ChevronDown size="S" /> : <ChevronRight size="S" />}
<Text>{labelName}</Text>
</Flex>
</Label>
<Ul style={{ display: `${toggle ? 'block' : 'none'}` }}>{children}</Ul>
</>
);
};
ag-psd
PSDファイルを読むためのJavaScriptライブラリです。Psd
型のオブジェクトとしてPSDファイルを扱うことができます。
{
"width": 300,
"height": 200,
"channels": 3,
"bitsPerChannel": 8,
"colorMode": 3,
"children": [
{
"top": 0,
"left": 0,
"bottom": 200,
"right": 300,
"blendMode": "normal",
"opacity": 1,
"transparencyProtected": false,
"hidden": true,
"clipping": false,
"name": "Layer 0",
"canvas": [Canvas]
},
{
"top": 0,
"left": 0,
"bottom": 0,
"right": 0,
"blendMode": "multiply",
"opacity": 1,
"transparencyProtected": true,
"hidden": false,
"clipping": false,
"name": "Layer 3",
"canvas": [Canvas]
}
],
"canvas": [Canvas]
}
canvas部分はHTML5のHTMLCanvasElement
です。ReactのJSXにはそのまま書けないため、toDataURL
してからimg
要素の中で表示させます。
<View gridArea="picture" width="100%" height="auto">
{cut.picture?.children
?.filter((child: Psd['children'], layerindex: number) => layerindex !== 0)
.map((child: Layer) => {
const src = child.canvas?.toDataURL('image/png', 0.4);
return (
<div
style={{
height: `${child.canvas && child.canvas.height * 0.12}px`,
width: `${child.canvas && child.canvas.width * 0.12}px`,
backgroundColor: '#fff',
position: 'relative',
}}
>
<img
style={{ transform: 'scale(0.12)', transformOrigin: 'left top' }}
src={src}
alt="cut"
/>
);
})}
</View>
ReactN
useGlobal
でグローバル状態管理を可能にするエクステンションです。使い方はuseState
とほぼ同じです。
import React, { setGlobal } from 'reactn';
import ReactDOM from 'react-dom';
import App from 'App';
import reportWebVitals from 'reportWebVitals';
import { Psd } from 'ag-psd';
const prtPsd: Psd = { width: 1, height: 1 };
const prtCut: Cut = {
picture: prtPsd,
};
setGlobal({
mode: 'Edit',
tool: new Set(['Select']),
cut: prtCut,
globalCuts: [prtCut],
globalPsds: [prtPsd],
globalFileName: '',
});
index.tsx
でsetGlobal
を宣言します。
import 'reactn';
declare module 'reactn/default' {
export interface State {
mode: string;
tool: Set<string> | undefined;
cut: Cut;
globalCuts: Cut[];
globalPsds: Psd[];
globalFileName: string;
}
}
global.d.ts
で型情報を記述します。
const CutContainer: React.FC = () => {
const prtPsd: Psd = { width: 1, height: 1 };
const prtCut: Cut = {
picture: prtPsd,
};
const [cuts, setCuts] = useState([prtCut]);
const globalCuts = useGlobal('globalCuts')[0];
const globalPsds = useGlobal('globalPsds')[0];
//略
}
あとはuseState
のようにコンポーネント内で記述するだけです。
react-hotkeys-hook
useHotKeys
でキーボードショートカットを簡単に実装できます。
const [selected, setSelected] = useGlobal('mode');
useHotkeys('e', () => {
setSelected('Edit');
});
useHotkeys('p', () => {
setSelected('Preview');
});
react-typescript-electron-sample-with-create-react-app-and-electron-builder
今回のアプリの雛形として使用しました。
メイン・レンダラーいずれのプロセスもホットリロード対応で、最高のDXです。