TL; DRïŒãããã®ãšããœãŒãã§ã¯ãReactãšReduxã§SVGèŠçŽ ãå¶åŸ¡ããŠã²ãŒã ãäœæããæ¹æ³ãåŠã³ãŸãã ãã®ã·ãªãŒãºã§åŸãããç¥èã«ãããã²ãŒã ã ãã§ãªãã¢ãã¡ãŒã·ã§ã³ãäœæã§ããŸãã ãã®ããŒãã§éçºããããœãŒã¹ã³ãŒãã®æçµããŒãžã§ã³ã¯GitHubã«ãããŸã ã

ã²ãŒã ã®ååïŒããšã€ãªã¢ã³ãå®¶ã«åž°ããïŒã
ãã®ã·ãªãŒãºã§éçºããã²ãŒã ã¯ããšã€ãªã¢ã³ãã²ããã¢ãŠã§ã€ããŒã ïŒ ã²ãŒã ã®ã¢ã€ãã¢ã¯ã·ã³ãã«ã§ããå°çã«äŸµå
¥ããããšããŠããããã©ã€ã³ã°ãã£ã¹ã¯ããæã¡èœãšãéããããŸãã ãããã®UFOãç Žå£ããã«ã¯ãããŠã¹ã«ã«ãŒãœã«ãåãããŠã¯ãªãã¯ããŠå€§ç ²ãçºå°ããå¿
èŠããããŸãã
èå³ãããå Žåã¯ã ããã§ã²ãŒã ã®æçµããŒãžã§ã³ãèŠã€ããŠå®è¡ã§ããŸãïŒ ãªã³ã¯åã-翻蚳è
ã®ã³ã¡ã³ã ïŒã ããããã²ãŒã ã«åå ããªãã§ãã ãããããªãã¯ä»äºãããŠããŸãïŒ
åææ¡ä»¶
èšäºãèªãã«ã¯ãWebéçºïŒäž»ã«JavaScriptã«ã€ããŠïŒãšNode.jsããã³NPMãããªã€ã³ã¹ããŒã«ãããŠããã³ã³ãã¥ãŒã¿ãŒã«ã€ããŠããçšåºŠã®ç¥èãå¿
èŠã§ãã ãã®ãã¥ãŒããªã¢ã«ã·ãªãŒãºãæ£åžžã«å®äºããããã«ãJavaScriptãReactãReduxãããã³SVGã®æ·±ãç¥èã¯å¿
èŠãããŸããã ãã ããäž»é¡ã«ããå Žåã¯ãäžéšã®éšåãšãããã®çžäºã®å¯Ÿå¿ãçè§£ãããããªããŸãã
ããã§ãããã®ã·ãªãŒãºã«ã¯ã泚ç®ãéããã«å€ãããããã¯ããããã説æããã®ã«åœ¹ç«ã€é¢é£èšäºãæçš¿ãããã³ããã¥ã¡ã³ããžã®ãªã³ã¯ãå«ãŸããŠããŸãã
å§ããåã«
åã®ã»ã¯ã·ã§ã³ã§ã¯Gitã«ã€ããŠèšåããŠããŸããã§ããããããã¯ããã€ãã®åé¡ã解決ããããã®åªããããŒã«ã§ããããšã¯æ³šç®ã«å€ããŸãã ãã¹ãŠã®ããã®éçºè
ã¯ãGitïŒãŸãã¯MercurialãSVNãªã©ã®å¥ã®ããŒãžã§ã³ç®¡çã·ã¹ãã ïŒã䜿çšããŠããããŒã ããããžã§ã¯ãäžã§ã掻åãé²ããŸãã
ããã¯ã¢ãããªãã§ãããžã§ã¯ããäœæããçç± ããªãããããæ¯æãå¿
èŠã¯ãããŸããã GitHub ïŒæé«ïŒïŒãBitBucket ïŒæªããªããæ£çŽèšã£ãŠïŒãªã©ã®ãµãŒãã¹ã䜿çšã㊠ãä¿¡é Œã§ããã¯ã©ãŠãã€ã³ãã©ã¹ãã©ã¯ãã£ã«ã³ãŒããä¿åã§ããŸãã
ãã®ãããªããŒã«ã䜿çšãããšãã³ãŒãã®å®å
šæ§ã«èªä¿¡ãæãŠãéçºããã»ã¹ãçŽæ¥ä¿é²ã§ããŸãã ããšãã°ãããã°ãã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ã®æ°ããããŒãžã§ã³ãäœæãããšãGitã䜿çšããŠç°¡åãªæé ãããã€ãå®è¡ããã ãã§ãç°¡åã«ä»¥åã®ããŒãžã§ã³ã®ã³ãŒãã«æ»ãããšãã§ããŸãã
ãã1ã€ã®éèŠãªå©ç¹ã¯ããã®ã·ãªãŒãºã®åã»ã¯ã·ã§ã³ã远跡ããæ®µéçã«éçºãããã³ãŒããã³ãããã§ããããšã§ãã ããã«ãããåã»ã¯ã·ã§ã³ã®åŸã«ã³ãŒãã®å€æŽãç°¡åã«ç¢ºèªã§ããŸãã ãã®ãã¥ãŒããªã¢ã«ãèªã¿ãªãããä»ããããªãã®äººçãæ¥œã«ããŠãã ããã
äžè¬çã«ã¯ãèªåã§Gitãã€ã³ã¹ããŒã«ããŠãã ããã ãŸãã GitHubã«ã¢ã«ãŠã³ãïŒãŸã æã£ãŠããªãå ŽåïŒãšãããžã§ã¯ããä¿åãããªããžããªãäœæããŸãã æ¬¡ã«ãåã»ã¯ã·ã§ã³ãå®äºããåŸã倿ŽããªããžããªãŒã«ã³ãããããŸãã ããã ããªãã®å€æŽãããã·ã¥ããããšãå¿ããªãã§ãã ãã ã
Create-React-Appã䜿çšãããããžã§ã¯ãã®ã¯ã€ãã¯ã¹ã¿ãŒã
ReactãReduxãSVGã䜿çšããŠã²ãŒã ãäœæããæåã®ã¹ãããã¯ã create-react-app
ã䜿çšcreate-react-app
ãŠãããžã§ã¯ãããã°ããéå§ããããšã§ãã ããããæ¢ã«ãåç¥ã®ãšããïŒããã§ãªãå Žåã¯å€§ããããšã¯ãããŸããïŒã create-react-appã¯FacebookããµããŒããããªãŒãã³ãœãŒã¹ããŒã«ã§ãããéçºè
ãããã«Reactã䜿ãå§ããã®ã«åœ¹ç«ã¡ãŸã Node.jsãšNPMãã€ã³ã¹ããŒã«ãããŠããå ŽåïŒåŸè
ã®ããŒãžã§ã³ã¯5.2以éã§ããå¿
èŠããããŸãïŒãcreate-react-appãã€ã³ã¹ããŒã«ããªããŠã䜿çšã§ããŸãã
# npx ( ) # create-react-app npx create-react-app aliens-go-home # cd aliens-go-home
ãã®ãããŒã«ãã¯ã次ã®ãããªæ§é ãäœæããŸãã
|- node_modules |- public |- favicon.ico |- index.html |- manifest.json |- src |- App.css |- App.js |- App.test.js |- index.css |- index.js |- logo.svg |- registerServiceWorker.js |- .gitignore |- package.json |- package-lock.json |- README.md
create-react-appããŒã«ã¯äººæ°ããããååã«ææžåãããŠãããã³ãã¥ããã£ã®ãµããŒããè¯å¥œã§ãã 詳现ã調ã¹ãããšã«èå³ãããå Žåã¯ãgithubã®create-react-appãªããžããªã確èªã ããŠãŒã¶ãŒã¬ã€ããèªãããšãã§ããŸã ã
çŸæç¹ã§ã¯ã以äžã«ãªã¹ããããŠãããã¡ã€ã«ãåãé€ãããšãã§ããŸããããã¯ãå°æ¥ãããã圹ã«ç«ããªããªãããã§ãã
App.css
ïŒ App
ã³ã³ããŒãã³ãã¯éèŠã§ãããã¹ã¿ã€ã«ã¯ä»ã®ã³ã³ããŒãã³ãã§å®çŸ©ãããŸããApp.test.js
ïŒãã¹ãã¯å¥ã®èšäºã®ãããã¯ãããããŸããã ä»ãã䜿çšããå¿
èŠã¯ãããŸãããlogo.svg
ïŒReactããŽã¯ãã®ã²ãŒã ã§ã¯äœ¿çšãããŸããã
ãããã®ãã¡ã€ã«ãåé€ãããšããããžã§ã¯ããéå§ããããšãããšãšã©ãŒãçºçããå ŽåããããŸãã ããã¯ãã ./src/App.js
ãã¡ã€ã«ãã2ã€ã®ãã€ã³ããŒãããåé€ããããšã§ç°¡åã«ä¿®æ£ã§ããŸãã
ãŸãã render()
ã¡ãœããããªãã¡ã¯ã¿ãªã³ã°ããããšã«ããïŒ
ã³ãããããããšãå¿ããªãã§ãã ããïŒ
ReduxãšPropTypesãã€ã³ã¹ããŒã«ãã
ãããžã§ã¯ãããããã€ãããããžã§ã¯ãããäžèŠãªãã¡ã€ã«ãåé€ããåŸãã¢ããªã±ãŒã·ã§ã³ã§å¯äžã®çã®ããŒã¿ãœãŒã¹ãšã㊠Redux ãæ§æããå¿
èŠããããŸãã PropTypesãã€ã³ã¹ããŒã«ããå¿
èŠããããŸãã ããã«ãããããã€ãã®äžè¬çãªãšã©ãŒãåé¿ã§ããŸãã 1ã€ã®ã³ãã³ãã§äž¡æ¹ã®ããŒã«ãã€ã³ã¹ããŒã«ã§ããŸãã
npm i redux react-redux prop-types
ã芧ã®ãšãããäžèšã®ã³ãã³ãã«ã¯3çªç®ã®NPMããã±ãŒãžã§ããreact-redux
ãŸãã ReduxãReactã§çŽæ¥äœ¿çšããããšã¯ãå§ãããŸããã react-reduxããã±ãŒãžã¯ ãããã©ãŒãã³ã¹ã®æé©åã®é¢åãªæååŠçââãè¡ããŸãã
Reduxã®æ§æãšPropTypesã®äœ¿çš
説æããããã±ãŒãžã䜿çšããŠãReduxã䜿çšããããã«ã¢ããªã±ãŒã·ã§ã³ãæ§æã§ããŸãã ããã¯ç°¡åã§ãã ã³ã³ãã ïŒã¹ããŒãã³ã³ããŒãã³ãïŒã ãã¬ãŒã³ããŒã·ã§ã³ã³ã³ããŒãã³ãïŒæããªã³ã³ããŒãã³ãïŒãããã³ã¬ãã¥ãŒãµãŒãäœæããã ãã§ãã ã¹ããŒãã³ã³ããŒãã³ããšæããªã³ã³ããŒãã³ãã®éãã¯ãæåã®ã³ã³ããŒãã³ããåçŽã«æããªã³ã³ããŒãã³ããReduxã«æ¥ç¶ããããšã§ãã äœæãã3çªç®ã®èŠçŽ ã§ããã¬ãã¥ãŒãµãŒã¯ãReduxã¹ãã¢ã®ã¡ã€ã³ã³ã³ããŒãã³ãã§ãã ãã®ã³ã³ããŒãã³ãã¯ãã¢ããªã±ãŒã·ã§ã³ã®ããŸããŸãªã€ãã³ãã«ãã£ãŠåŒãèµ·ãããããã¢ã¯ã·ã§ã³ãïŒã¢ã¯ã·ã§ã³ïŒãå®è¡ãããããã®ã¢ã¯ã·ã§ã³ã«åºã¥ããŠãã¹ãã¢ãïŒããŒã¿ãœãŒã¹ïŒã倿Žããæ©èœãé©çšããŸãã
ãã®ãã¹ãŠã«ã€ããŠããããªãå Žåã¯ãã³ã³ããŒãã³ãïŒéãïŒããã³ã³ã³ãããŒïŒã¹ããŒãïŒã³ã³ããŒãã³ãã«ã€ããŠè©³ãã説æããŠãããã®èšäºãèªãããšãã§ããŸãã ãŸãã ã¢ã¯ã·ã§ã³ã²ãŒã ã ã¬ãã¥ãŒãµãŒ ãããã³ã¹ãã¢ã«æ
£ããããã«Reduxå®çšã¬ã€ããéããŸã ã ãããã®æŠå¿µãåŠã¶ããšã匷ããå§ãããŸããã远å ã®èªæžã«æ©ãŸãããããšãªãå匷ãç¶ããããšãã§ããŸãã
ãã®èŠçŽ ã¯ä»ã®èŠçŽ ããç¬ç«ããŠããããïŒå®éã«ã¯éã®ããšãåœãŠã¯ãŸãïŒãã¬ãã¥ãŒãµãŒãäœæããŠããã»ã¹ãéå§ããæ¹ã䟿å©ã§ãã æ§é ãç¶æããã«ã¯ã reducers
ãšããæ°ãããã£ã¬ã¯ããªãäœæãããã®äžã«src
ãé
眮ãã index.js
ãšãããã¡ã€ã«ã远å ããŸãã ãã®ãã¡ã€ã«ã«ã¯ã次ã®ãœãŒã¹ã³ãŒããå«ãŸããå ŽåããããŸãã
const initialState = { message: `React Redux , ?`, }; function reducer(state = initialState) { return state; } export default reducer;
ãããã£ãŠãReducerã¯ãReactãšReduxãç°¡åã«çµ±åã§ããã¡ãã»ãŒãžã§ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ãåæåããã ãã§ãã ãã®ãã¡ã€ã«ã§ã¯ãããã«ã¢ã¯ã·ã§ã³ã®å®çŸ©ãšåŠçãéå§ããŸãã
ãã®åŸã App
ã³ã³ããŒãã³ãããªãã¡ã¯ã¿ãªã³ã°ããŠããã®ã¡ãã»ãŒãžããŠãŒã¶ãŒã«è¡šç€ºã§ããŸãã PropTypes
ã䜿çšããŸãã ãããè¡ãã«ã¯ãã ./src/App.js
ãã¡ã€ã«ãéãããã®å
å®¹ãæ¬¡ã®ããã¹ãã«çœ®ãæããŸãã
import React, {Component} from 'react'; import PropTypes from 'prop-types'; class App extends Component { render() { return ( <div className="App"> <h1>{this.props.message}</h1> </div> ); } } App.propTypes = { message: PropTypes.string.isRequired, }; export default App;
ã芧ã®ãšããã PropTypes
ã䜿çšãããšãã³ã³ããŒãã³ããæåŸ
ããåãæ±ºå®ããã®PropTypes
éåžžã«ç°¡åã§ãã App
ã³ã³ããŒãã³ãã®PropTypes
ããããã£ãå¿
èŠãªãã©ã¡ãŒã¿ãŒã§èšå®ããã ãã§ãã ãããã¯ãŒã¯ã«ã¯ãåºæ¬çãªPropTypes
å®çŸ©ãšé«åºŠãªPropTypes
å®çŸ©ãäœæããæ¹æ³ã説æããããŒãã·ãŒãããããŸãïŒããšãã°ã ãã ã ãããããã³ãã ïŒ å¿
èŠã«å¿ããŠãã§ãã¯ããŠãã ããã
store
ïŒ store
ïŒã®åæç¶æ
ãšApp
ã³ã³ããŒãã³ãã®è¡šç€ºå
å®¹ãæ±ºå®ãããããããã®èŠçŽ ããªã³ã¯ããå¿
èŠããããŸãã ãããã³ã³ããã®ç®çã§ãã æ§é å
ã«ã³ã³ãããäœæããã«ã¯ã src
ãã£ã¬ã¯ããªå
ã«ontainers
ãšããååã®ãã£ã¬ã¯ããªãäœæããå¿
èŠããããŸãã ãã®åŸãæ°ãããã£ã¬ã¯ããªã§ã Game.js
ãã¡ã€ã«å
ã«Game
ãšããã³ã³ããŒãã³ããäœæããŸãã ãã®ã³ã³ããã¯ã react-redux
connect
æ©èœã䜿çšããŠã state.message
ãApp
ã³ã³ããŒãã³ãã®ã¡ãã»ãŒãžãã©ã¡ãŒã¿ãŒã«æž¡ããŸãã
import { connect } from 'react-redux'; import App from '../App'; const mapStateToProps = state => ({ message: state.message, }); const Game = connect( mapStateToProps, )(App); export default Game;
æçµæ®µéã«é²ã¿ãŸãã ãã¹ãŠããªã³ã¯ããæåŸã®ã¹ãããã¯ã. ./src/index.js
ãã¡ã€ã«ããªãã¡ã¯ã¿ãªã³ã°ããŠRedux ã¹ãã¢ãåæåãã Game
ã³ã³ãããŒã«è»¢éããŸãïŒã¡ãã»ãŒãžãåä¿¡ãã App
ããããéä¿¡ïŒ ãã¹ ïŒããŸãïŒã 次ã®ã³ãŒãã¯ããªãã¡ã¯ã¿ãªã³ã°åŸã®./src/index.js
ãã¡ã€ã«ã®å€èгã瀺ããŠããŸãã
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import './index.css'; import Game from './containers/Game'; import reducer from './reducers'; import registerServiceWorker from './registerServiceWorker'; const store = createStore( reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), ); ReactDOM.render( <Provider store={store}> <Game /> </Provider>, document.getElementById('root'), ); registerServiceWorker();
ãã£ãïŒ ãã¹ãŠãã©ã®ããã«æ©èœããããè©äŸ¡ããã«ã¯ãã¿ãŒããã«ã§ãããžã§ã¯ãã®ã«ãŒãã«ç§»åããŠnpm start
ãå®è¡ããŸãã ãããã£ãŠãã¢ããªã±ãŒã·ã§ã³ãéçºã¢ãŒãïŒ dev-mode ïŒã§å®è¡ãããšãããã©ã«ãã®ãã©ãŠã¶ãŒã§éããŸãã
Reactã§SVGã³ã³ããŒãã³ããäœæãã
ãã®ã·ãªãŒãºã§ã¯ãReactã䜿çšããŠSVGã³ã³ããŒãã³ããç°¡åã«äœæã§ããããšã«æè¬ããŸãã å®éãReactã³ã³ããŒãã³ããHTMLãšSVGã®ã©ã¡ãã§äœæããŠãéãã¯ã»ãšãã©ãããŸããã äž»ãªéãã¯ãæ°ããèŠçŽ ãSVGã«å°å
¥ããããããã®èŠçŽ ãSVGãã£ã³ãã¹ã«æç»ãããããšã§ãã
SVGãšReactã䜿çšããŠç¬èªã®ã³ã³ããŒãã³ããäœæããåã«ãSVGã«ã€ããŠç°¡åã«çè§£ããŠãããšåœ¹ç«ã¡ãŸãã
SVGã®æŠèŠ
SVGã¯ãæãã¯ãŒã«ã§æè»ãªWebæšæºã®1ã€ã§ãã SVGã¯ãScalable Vector Graphicsã®ç¥ã§ãéçºè
ã2次å
ã®ãã¯ã¿ãŒã°ã©ãã£ãã¯ã¹ãèšè¿°ããããšãã§ããããŒã¯ã¢ããèšèªã§ãã SVGã¯HTMLã«éåžžã«äŒŒãŠããŸãã ã©ã¡ããXMLããŒã¹ã®ããŒã¯ã¢ããèšèªã§ãããCSSãDOMãªã©ã®ä»ã®WebæšæºãšããŸã飿ºããŸãã CSSã«ãŒã«ã¯ãã¢ãã¡ãŒã·ã§ã³ãå«ãSVGãšHTMLã®äž¡æ¹ã«çããé©çšãããŸãã
ãã®ã·ãªãŒãºã§ã¯ãReactã䜿çšããŠã12ãè¶
ããSVGã³ã³ããŒãã³ããäœæããŸãã ã²ãŒã ãªããžã§ã¯ãïŒç ²åŒŸãçºå°ããå€§ç ²ïŒãäœæããã«ã¯ãSVGèŠçŽ ãæ§æïŒã°ã«ãŒãåïŒããå¿
èŠãããããŸãã
SVGã®ãã詳现ãªèª¿æ»ã¯ããã®èšäºã®ãã¬ãŒã ã¯ãŒã¯å
ã§ã¯äžå¯èœã§ãããé·ãããŸãã SVGããŒã¯ã¢ããèšèªã«ã€ããŠè©³ããç¥ãããå Žåã¯ãMozillaãæäŸãããã¥ãŒããªã¢ã«ãš ããã®èšäºã§ç޹ä»ããSVG座æšç³»ã«é¢ããè³æããèªã¿ ãã ãã ã
ãã ããç¬èªã®ã³ã³ããŒãã³ãã®äœæãéå§ããåã«ãSVGã®ããã€ãã®ç¹æ§ãåŠã¶ããšãéèŠã§ãã 第1ã«ãSVGãDOMãšçµã¿åãããŠäœ¿çšââãããšãéçºè
ã¯ãäœãããããããšãã§ããŸãã Reactã§SVGã䜿çšããã®ã¯éåžžã«ç°¡åã§ãã
第äºã«ãSVG座æšç³»ã¯ãã«ã«ãå¹³é¢ã«äŒŒãŠãããéããŸã«ãªã£ãŠããŸãã ãããã£ãŠãããã©ã«ãã§ã¯ãè² ã®å€ã¯X軞ã®äžã«åçŽã«è¡šç€ºãããŸãããã®å Žåãæ°Žå¹³å€ã¯ãã«ã«ãå¹³é¢ãšåãããã«é
眮ãããŸããã€ãŸããè² ã®å€ã¯Y軞ã®å·ŠåŽã«é
眮ãããŸãããã®åäœã¯ãSVGãã£ã³ãã¹ã«å€æãé©çšããããšã§ç°¡åã«å€æŽã§ããŸã ã ãã ããéçºè
éã®æ··ä¹±ãé¿ããããã«ãããã©ã«ãèšå®ã䜿çšããããšããå§ãããŸãã ããã«æ
£ããã§ãããã
æåŸã«ãSVGã¯å€ãã®æ°ããèŠçŽ ïŒ circle
ã rect
path
ïŒãå°å
¥ããããšã«æ³šæããŠãã ããã ãããã®èŠçŽ ã䜿çšããã«ã¯ãHTMLèŠçŽ å
ã§å®çŸ©ããã ãã§ã¯äžååã§ãã æåã«ããã¹ãŠã®SVGã³ã³ããŒãã³ããæç»ããsvg
èŠçŽ ïŒãã£ã³ãã¹ïŒãå®çŸ©ããå¿
èŠããããŸãã
SVGããã¹èŠçŽ ãäžæ¬¡ããžã§æ²ç·
SVGèŠçŽ ã®æç»ã¯ã3ã€ã®æ¹æ³ã§å®è¡ã§ããŸãã ãŸãã rect
ã circle
line
ãªã©ã®åºæ¬çãªèŠçŽ ã䜿çšã§ããŸãã ãã ãããããã®èŠçŽ ã¯ç¹ã«æè»ã§ã¯ãããŸããã ååïŒé·æ¹åœ¢ãåãç·ïŒã«åŸã£ãŠåçŽãªå³åœ¢ãæç»ã§ããŸãã
2çªç®ã®æ¹æ³ã¯ãåºæ¬èŠçŽ ãçµã¿åãããŠãããè€éãªåœ¢ç¶ãååŸããããšã§ãã ããšãã°ãå®¶ãæãããã«ãåãèŸºïŒæ£æ¹åœ¢ãååŸïŒãš2æ¬ã®ç·ã§é·æ¹åœ¢ïŒ rect
ïŒã䜿çšã§ããŸãã ãã ãããã®ã¢ãããŒãã¯äŸç¶ãšããŠéåžžã«å³ããå¶éãããŠããŸãã
3çªç®ã®æãæè»ãªæ¹æ³ã¯ã ãã¹èŠçŽ ã䜿çšããããšã§ã ã ãã®ãªãã·ã§ã³ã«ãããéçºè
ã¯ããªãè€éãªãã©ãŒã ãäœæã§ããŸãã å³ãæç»ããã«ã¯ããã©ãŠã¶ã«ç¹å®ã®ã³ãã³ããæå®ããŸãã ããšãã°ããLããæç»ããã«ã¯ã3ã€ã®ã³ãã³ããå«ãpath
èŠçŽ ãäœæã§ããŸãã
M 20 20
ïŒ M 20 20
åŸã«å®çŸ©ãããXããã³Y座æšã«ããã³ããç§»åãããã©ãŠã¶ãžã®ã³ãã³ãïŒããªãã¡20, 20
ïŒãV 80
ïŒãã©ãŠã¶ã«ã³ãã³ããå®è¡ããŠãYè»žã«æ²¿ã£ãŠåã®ãã€ã³ãããäœçœ®80
ã«ç·ãåŒããŸããH 50
ïŒåã®ãã€ã³ãããXè»žã«æ²¿ã£ãŠäœçœ®50
ã«ç·ãåŒãããã«ãã©ãŠã¶ãŒã«æç€ºããŸãã
<svg> <path d="M 20 20 V 80 H 50" stroke="black" stroke-width="2" fill="transparent" /> </svg>
Path
èŠçŽ ã¯ä»ã®å€ãã®ã³ãã³ããåãå
¥ããŸãã æãéèŠãªãã®ã®1ã€ã¯ã3次ããžã§æ²ç·ã®ããŒã ã§ãã 2ã€ã®åºæºç¹ãš2ã€ã®å¶åŸ¡ç¹ã䜿çšããŠããæ»ãããªãæ²ç·ã远å ã§ããŸãã
ãåãã€ã³ãã®3次ããžã§æ²ç·ã¯2ã€ã®å¶åŸ¡ç¹ãåããŸãããããã£ãŠã3次ããžã§æ²ç·ãäœæããã«ã¯ã3ã€ã®åº§æšã»ãããæå®ããå¿
èŠããããŸããæåŸã®åº§æšã»ããã¯ãæ²ç·ã®çµäºç¹ã瀺ããŸããä»ã®2ã€ã®ã»ããã¯å¶åŸ¡ç¹ã§ããåºæ¬çã«ãã³ã³ãããŒã«ãã€ã³ãã¯ç¹å®ã®ãã€ã³ãã§ã®ã©ã€ã³ã®åŸé
ã衚ããŸããããžã§é¢æ°ã¯ãã©ã€ã³ã®å
é ã«èšå®ããåŸé
ããæåŸã«èšå®ããåŸé
ãŸã§ã®æ»ãããªæ²ç·ãäœæããŸãã -Mozilla Developer Network
ããšãã°ããUããæç»ããã«ã¯ãæ¬¡ã®æé ãå®è¡ããŸãã
<svg> <path d="M 20 20 C 20 110, 110 110, 110 20" stroke="black" fill="transparent"/> </svg>
ãã®å Žåã path
èŠçŽ ã«æž¡ãããã³ãã³ãã¯ãã©ãŠã¶ãŒã«æ¬¡ã®ããšãäŒããŸãã
- ãã€ã³ã
20,20
ããæç»ãéå§ããŸãã - æåã®å¶åŸ¡ç¹ã®åº§æšïŒ
20, 110
; - 2çªç®ã®å¶åŸ¡ç¹ã®åº§æšïŒ
110, 110
; - æ²ç·ã®çµç¹ã®åº§æšïŒ
110 20
;
3次ããžãšæ²ç·ã®åäœåçããŸã çè§£ããŠããªãå Žåã¯ãçµ¶æããªãã§ãã ããã ãã®ã·ãªãŒãºã§ç·Žç¿ããæ©äŒããããŸãã ããã«ãã€ã³ã¿ãŒãããäžã§ãã®æ©èœã«é¢ããå€ãã®ã¬ã€ããèŠã€ããããšãã§ãã JSFiddleãCodepenãªã©ã®ããŒã«ã§ãã€ã§ãç·Žç¿ã§ããŸãã
Canvasã³ã³ããŒãã³ãã®äœæ
ïŒããã¯<canvas></canvas>
ã«é¢ãããã®ã§ã¯ãªããCanvasåå¿ã³ã³ããŒãã³ãïŒãã·ã¢èªã®ãã£ã³ãã¹ïŒ-翻蚳è
ã®ã³ã¡ã³ãïŒ
ãããžã§ã¯ãã®æ§é ãäœæããSVGã®åºæ¬ãåŠç¿ããããã²ãŒã ã®äœæãéå§ã§ããŸãã äœæããæåã®èŠçŽ ã¯ãã²ãŒã èŠçŽ ã®æç»ã«äœ¿çšãããSVGãã£ã³ãã¹ã§ãã
ãã®ã³ã³ããŒãã³ãã¯ããã¬ãŒã³ããŒã·ã§ã³ïŒæããªïŒãšããŠç¹åŸŽä»ããããŸãã ãããã£ãŠã. ./src
ãã£ã¬ã¯ããªå
ã«./src
ãã£ã¬ã¯ããªãäœæããŠãæ°ããã³ã³ããŒãã³ããšãã®ãå
åŒãïŒ é£æ¥/åèŠçŽ -翻蚳è
ã³ã¡ã³ã ïŒãä¿åã§ããŸãã ãããããªãã®ãã£ã³ãã¹ã«ãªãã®ã§ã Canvas
ãããèªç¶ãªååãæãä»ãã®ã¯é£ããã§ãã ./src/components/
ãã£ã¬ã¯ããªå
ã«./src/components/
ãšããæ°ãããã¡ã€ã«ãäœæããæ¬¡ã®ã³ãŒãã远å ããŸãã
import React from 'react'; const Canvas = () => { const style = { border: '1px solid black', }; return ( <svg id="aliens-go-home-canvas" preserveAspectRatio="xMaxYMax none" style={style} > <circle cx={0} cy={0} r={50} /> </svg> ); }; export default Canvas;
Canvas
ã³ã³ããŒãã³ãã䜿çšããããã«App
ã³ã³ããŒãã³ããæžãçŽããŸãã
import React, {Component} from 'react'; import Canvas from './components/Canvas'; class App extends Component { render() { return ( <Canvas /> ); } } export default App;
ãããžã§ã¯ããå®è¡ãïŒ npm start
ïŒãã¢ããªã±ãŒã·ã§ã³ããã¹ããããšããã©ãŠã¶ãŒã¯ãã®åã®4åã®1ã ããæç»ããããšãããããŸãã ããã¯ãããã©ã«ãã§ã¯ãåç¹ãç»é¢ã®å·Šäžé
ã«ããããã§ãã ããã«ã svg
èŠçŽ ãç»é¢å
šäœãå æããŠããªãããšãããããŸãã
ããé¢çœããŠäŸ¿å©ãªã³ã³ãããŒã«ãäœæããã«ã¯ããã£ã³ãã¹ïŒ <Canvas/>
ïŒãå
šç»é¢è¡šç€ºã«é©ãããã®ã«ããŸãã éå§ç¹ãX軞ã®äžå¿ã«ç§»åããŠãäžéšã«è¿ã¥ããããšãã§ããŸãïŒåŸã§éããªãªãžãã«ã«è¿œå ããŸãïŒã äž¡æ¹ã®æ¡ä»¶ãæºããã«ã¯ã2ã€ã®ãã¡ã€ã«ã倿Žããå¿
èŠããããŸãïŒ ./src/components/Canvas.jsx
/ ./src/index.css
/ ./src/components/Canvas.jsx
ãš./src/index.css
ã
Canvas
ã®å
容ã眮ãæããããšããå§ããŠã次ã®ã³ãŒããé©çšããŸãã
import React from 'react'; const Canvas = () => { const viewBox = [window.innerWidth / -2, 100 - window.innerHeight, window.innerWidth, window.innerHeight]; return ( <svg id="aliens-go-home-canvas" preserveAspectRatio="xMaxYMax none" viewBox={viewBox} > <circle cx={0} cy={0} r={50} /> </svg> ); }; export default Canvas;
ãã®ããŒãžã§ã³ã®ã³ã³ããŒãã³ãã§ã¯ã svg
ã¿ã°ã®viewBox屿§ãèšå®ããŸã ã ãã®å±æ§ã«ããããã£ã³ãã¹ãšãã®ã³ã³ãã³ããç¹å®ã®ã³ã³ããïŒãã®å ŽåããŠã£ã³ããŠ/ãã©ãŠã¶ã®å
åŽã®é åïŒã«å¯Ÿå¿ããå¿
èŠãããããšã決å®ã§ããŸãã ã芧ã®ãšãããviewBox屿§ã¯4ã€ã®æ°åã§æ§æãããŠããŸãã
min-x
ïŒãã®å€ã¯ããŠãŒã¶ãŒãèŠãããšãã§ãã巊端ã®ãã€ã³ããå®çŸ©ããŸãã ãããã£ãŠãç»é¢ã®äžå¿ã«è¡šç€ºããã軞ïŒããã³åïŒãååŸããããã«ãç»é¢ã®å¹
ãã-ãèšå·ã§2ã§å²ã£ãŠïŒ window.innerWidth / -2
ïŒå±æ§å€ïŒ min-x
ïŒãååŸããŸãã ãã£ã³ãã¹ã座æšåç¹ã®äž¡åŽã«åãæ°ã®ãã€ã³ãã衚瀺ããããã«ïŒ -2
ïŒã§å²ãå¿
èŠãããããšã«æ³šæããŠãã ãããmin-y
ïŒãã£ã³ãã¹äžã®æé«ç¹ãå®çŸ©ããŸãã ããã§ã¯ã window.innerHeight
ã®å€ã100
ããæžç®ããŠãYã®å
é ããç¹å®ã®é åïŒ 100
ãã€ã³ãïŒãèšå®ããå¿
èŠããããŸããwidth
ãšheight
ïŒãŠãŒã¶ãŒãç»é¢ã«è¡šç€ºããX軞ãšYè»žã«æ²¿ã£ããã€ã³ãã®æ°ã決å®ããŸãã
viewBox
屿§ã®å®çŸ©ã«å ããŠãæ°ããããŒãžã§ã³ã§ã¯preserveAspectRatioãšãã屿§ãèšå®ããŸãã ãã£ã³ãã¹ãšãã®èŠçŽ ã®åäžãªã¹ã±ãŒãªã³ã°ã匷å¶ããããã«ã xMaxYMax none
ã䜿çšãxMaxYMax none
ã
ïŒ preserveAspectRatio
ã€ã³ã¹ããŒã«ãããšãreactããèŠåãçºçããŸãã-翻蚳è
ã®ã³ã¡ã³ã ïŒ
ãã£ã³ãã¹ããªãã¡ã¯ã¿ãªã³ã°ããåŸã次ã®ã«ãŒã«ã./src/index.css
ãã¡ã€ã«ã«è¿œå ããå¿
èŠããããŸãã
html, body { overflow: hidden; height: 100%; }
ããã¯ã html
ããã³body
èŠçŽ ïŒã¿ã°ïŒãã¹ã¯ããŒã«ãé衚瀺ïŒããã³ç¡å¹ïŒã«ããããã«è¡ãããŸãã ããã«ãã¢ã€ãã ã¯å
šç»é¢ã§è¡šç€ºãããŸãã
ä»ããã¢ããªã±ãŒã·ã§ã³ããã§ãã¯ãããšãåã¯ç»é¢ã®äžéšã®äžå€®ã®æ°Žå¹³æ¹åã«ããããšãããããŸãã
Skyã³ã³ããŒãã³ãã®äœæ
ãã£ã³ãã¹ãå
šç»é¢è§£å床ã«èšå®ããåç¹ããã®äžå¿ã«é
眮ããããå®éã®ã²ãŒã èŠçŽ ã®äœæãéå§ã§ããŸãã ã²ãŒã ã®èæ¯èŠçŽ ã§ãã空ã®èšèšããå§ããããšãã§ããŸãã ãããè¡ãã«ã¯ã次ã®ã³ãŒãã䜿çšããŠ./src/components/
ãã£ã¬ã¯ããªã«Sky.jsx
ãšããæ°ãããã¡ã€ã«ãäœæããŸãã
import React from 'react'; const Sky = () => { const skyStyle = { fill: '#30abef', }; const skyWidth = 5000; const gameHeight = 1200; return ( <rect style={skyStyle} x={skyWidth / -2} y={100 - gameHeight} width={skyWidth} height={gameHeight} /> ); }; export default Sky;
ã²ãŒã ã«ãã®ãããªå€§ããªé åãæå®ãããŠããçç±ïŒå¹
5000
ããã³é«ã1200
ïŒãçåã«æããããããŸããã å®éããã®ã²ãŒã ã®å¹
ã¯éèŠã§ã¯ãããŸããã ç»é¢ãµã€ãºãã«ããŒããã®ã«ååãªæ°ã ãèšå®ããå¿
èŠããããŸãã
次ã«ãé«ããéèŠã§ãã ãŠãŒã¶ãŒã®ç»é¢ã®è§£å床ãšåãã«é¢ä¿ãªããã£ã³ãã¹äžã«1200ãã€ã³ããèšå®ãããããããã«ããã²ãŒã ã®äžè²«æ§ã確ä¿ããããã¹ãŠã®ãŠãŒã¶ãŒã«åãé åã衚瀺ãããŸãã ãããã£ãŠããã©ã€ã³ã°ãã£ã¹ã¯ã衚瀺ãããå Žæãšãæå®ããããã€ã³ããééããæéãæ±ºå®ã§ããŸãã
ãã£ã³ãã¹ã«ç©ºïŒ Sky
ã³ã³ããŒãã³ãïŒã衚瀺ããã«ã¯ããšãã£ã¿ãŒã§Canvas.jsx
ãã¡ã€ã«ãéãã Canvas.jsx
ããã«ä¿®æ£ããŸãã
import React from 'react'; import Sky from './Sky'; const Canvas = () => { const viewBox = [window.innerWidth / -2, 100 - window.innerHeight, window.innerWidth, window.innerHeight]; return ( <svg id="aliens-go-home-canvas" preserveAspectRatio="xMaxYMax none" viewBox={viewBox} > <Sky /> <circle cx={0} cy={0} r={50} /> </svg> ); }; export default Canvas;
ããã§ã¢ããªã±ãŒã·ã§ã³ããã¹ããããšïŒ npm start
ïŒãåã¯ãŸã äžéšã®äžå€®ã«ãããèæ¯ãéã«ãªã£ãŠããããšãããããŸãã
泚 ïŒ circle
èŠçŽ ã®åŸã«Sky
èŠçŽ ã远å ãããšã circle
ã¯è¡šç€ºãããªããªããŸãã ããã¯ãSVG ã z-index
ãµããŒãããŠããªãããã§ãã SVGã¯ããªã¹ããããŠããé åºã«åŸã£ãŠãã©ã®èŠçŽ ããããé«ããããå€å¥ããŸãã ã€ãŸãã Sky
åŸã«circle
èŠçŽ ãèšè¿°ããŠãWebãã©ãŠã¶ãŒãéãèæ¯ã®äžã«è¡šç€ºããããã«ããå¿
èŠããããŸãã
Groundã³ã³ããŒãã³ãã®äœæ
Sky
ã³ã³ããŒãã³ããäœæãããã Ground
ã³ã³ããŒãã³ãã®äœæã«é²ã¿ãŸãã ãããè¡ãã«ã¯ãã Ground.jsx
ãã£ã¬ã¯ããªã«æ°ããGround.jsx
ãã¡ã€ã«ãäœæããæ¬¡ã®ã³ãŒãã远å ããŸãã
import React from 'react'; const Ground = () => { const groundStyle = { fill: '#59a941', }; const division = { stroke: '#458232', strokeWidth: '3px', }; const groundWidth = 5000; return ( <g id="ground"> <rect id="ground-2" data-name="ground" style={groundStyle} x={groundWidth / -2} y={0} width={groundWidth} height={100} /> <line x1={groundWidth / -2} y1={0} x2={groundWidth / 2} y2={0} style={division} /> </g> ); }; export default Ground;
ãã®èŠçŽ ã«ã€ããŠçŽ æŽãããããšã¯äœããããŸããã ããã¯ã rect
èŠçŽ ãšline
èŠçŽ ã®åãªãåæã§ãã ãã ãããæ°ã¥ãã®ããã«ããã®èŠçŽ ã«ã¯ãå¹
ãæ±ºå®ããå€5000
宿°ãå«ãŸããŠããŸãã ãããã£ãŠããã®ãããªã°ããŒãã«å®æ°ãä¿åãããã¡ã€ã«ãäœæãããšããã§ãããã
ãã®çµè«ã«éããã®ã§ãã ./src/
ãã£ã¬ã¯ããªå
ã«utils
ãšããæ°ãããã£ã¬ã¯ããªãäœæãããã®æ°ãããã£ã¬ã¯ããªå
ã«constants.js
ãšãããã¡ã€ã«ãäœæããŸãã çŸæç¹ã§ã¯ããã®ãã¡ã€ã«ã«ä¿åãã宿°ã¯1ã€ã ãã§ãã
Sky
Ground
, .
Ground
(, (.. Sky
circle
)). - , , .
Cannon ()
, . . , , . . , , .
, : , . , d
path
, : M 20 20 C 20 110, 110 110, 110 20
.
, formula.js
./src/utils/
, :
export const pathFromBezierCurve = (cubicBezierCurve) => { const { initialAxis, initialControlPoint, endingControlPoint, endingAxis, } = cubicBezierCurve; return ` M${initialAxis.x} ${initialAxis.y} c ${initialControlPoint.x} ${initialControlPoint.y} ${endingControlPoint.x} ${endingControlPoint.y} ${endingAxis.x} ${endingAxis.y} `; };
, () ( initialAxis
, initialControlPoint
, endControlPoint
, endAxis
) cubicBezierCurve
, .
, . : CannonBase
() CannonPipe
().
CannonBase
CannonBase.jsx
./src/components
:
import React from 'react'; import { pathFromBezierCurve } from '../utils/formulas'; const CannonBase = (props) => { const cannonBaseStyle = { fill: '#a16012', stroke: '#75450e', strokeWidth: '2px', }; const baseWith = 80; const halfBase = 40; const height = 60; const negativeHeight = height * -1; const cubicBezierCurve = { initialAxis: { x: -halfBase, y: height, }, initialControlPoint: { x: 20, y: negativeHeight, }, endingControlPoint: { x: 60, y: negativeHeight, }, endingAxis: { x: baseWith, y: 0, }, }; return ( <g> <path style={cannonBaseStyle} d={pathFromBezierCurve(cubicBezierCurve)} /> <line x1={-halfBase} y1={height} x2={halfBase} y2={height} style={cannonBaseStyle} /> </g> ); }; export default CannonBase;
. - ( #75450e
) "" - ( #a16012
).
CannonPipe
CannonBase
. , pathFromBezierCurve
, . , transform
.
CannonPipe.jsx
./src/components/
:
import React from 'react'; import PropTypes from 'prop-types'; import { pathFromBezierCurve } from '../utils/formulas'; const CannonPipe = (props) => { const cannonPipeStyle = { fill: '#999', stroke: '#666', strokeWidth: '2px', }; const transform = `rotate(${props.rotation}, 0, 0)`; const muzzleWidth = 40; const halfMuzzle = 20; const height = 100; const yBasis = 70; const cubicBezierCurve = { initialAxis: { x: -halfMuzzle, y: -yBasis, }, initialControlPoint: { x: -40, y: height * 1.7, }, endingControlPoint: { x: 80, y: height * 1.7, }, endingAxis: { x: muzzleWidth, y: 0, }, }; return ( <g transform={transform}> <path style={cannonPipeStyle} d={pathFromBezierCurve(cubicBezierCurve)} /> <line x1={-halfMuzzle} y1={-yBasis} x2={halfMuzzle} y2={-yBasis} style={cannonPipeStyle} /> </g> ); }; CannonPipe.propTypes = { rotation: PropTypes.number.isRequired, }; export default CannonPipe;
CannonBase
CannonPipe
. :
import React from 'react'; import Sky from './Sky'; import Ground from './Ground'; import CannonBase from './CannonBase'; import CannonPipe from './CannonPipe'; const Canvas = () => { const viewBox = [window.innerWidth / -2, 100 - window.innerHeight, window.innerWidth, window.innerHeight]; return ( <svg id="aliens-go-home-canvas" preserveAspectRatio="xMaxYMax none" viewBox={viewBox} > <Sky /> <Ground /> <CannonPipe rotation={45} /> <CannonBase /> </svg> ); }; export default Canvas;
:

! ( Sky
Ground
) ( CannonBase
+ CannonPipe
). . , - , . onmousemove
, .. , , .
, , CannonPipe
. onmousemove
, - () . ( ), ( Redux).
Redux action ( , ) ( â ). , Actions
./src/
. index.js
, :
export const MOVE_OBJECTS = 'MOVE_OBJECTS'; export const moveObjects = mousePosition => ({ type: MOVE_OBJECTS, mousePosition, });
: MOVE_OBJECTS , . .
( index.js
./src/reducers/
):
import { MOVE_OBJECTS } from '../actions'; import moveObjects from './moveObjects'; const initialState = { angle: 45, }; function reducer(state = initialState, action) { switch (action.type) { case MOVE_OBJECTS: return moveObjects(state, action); default: return state; } } export default reducer;
, , MOVE_OBJECTS
, moveObjects
. , , ( initial state ) , angle
45
. .
, moveObjects
. , , , . moveObjects.js
./src/reducers/
:
import { calculateAngle } from '../utils/formulas'; function moveObjects(state, action) { if (!action.mousePosition) return state; const { x, y } = action.mousePosition; const angle = calculateAngle(0, 0, x, y); return { ...state, angle, }; } export default moveObjects;
, x
y
mousePosition
calculateAngle
. , , ( ) .
, , calculateAngle
formula.js
, ? , , , , StackExchange , , . , formula.js
( ./src/utils/formulas
):
export const radiansToDegrees = radians => ((radians * 180) / Math.PI); // https://math.stackexchange.com/questions/714378/find-the-angle-that-creating-with-y-axis-in-degrees export const calculateAngle = (x1, y1, x2, y2) => { if (x2 >= 0 && y2 >= 0) { return 90; } else if (x2 < 0 && y2 >= 0) { return -90; } const dividend = x2 - x1; const divisor = y2 - y1; const quotient = dividend / divisor; return radiansToDegrees(Math.atan(quotient)) * -1; };
: atan
, Math
, . . radiansToDegrees
.
, , . Redux, action ( ) moveObjects
props ( ) App
. Game
. Game.js
( ./src/containers
) :
import { connect } from 'react-redux'; import App from '../App'; import { moveObjects } from '../actions/index'; const mapStateToProps = state => ({ angle: state.angle, }); const mapDispatchToProps = dispatch => ({ moveObjects: (mousePosition) => { dispatch(moveObjects(mousePosition)); }, }); const Game = connect( mapStateToProps, mapDispatchToProps, )(App); export default Game;
( mapStateToProps
mapDispatchToProps
) App
props
. App.js
( ./src/
) :
import React, {Component} from 'react'; import PropTypes from 'prop-types'; import { getCanvasPosition } from './utils/formulas'; import Canvas from './components/Canvas'; class App extends Component { componentDidMount() { const self = this; setInterval(() => { self.props.moveObjects(self.canvasMousePosition); }, 10); } trackMouse(event) { this.canvasMousePosition = getCanvasPosition(event); } render() { return ( <Canvas angle={this.props.angle} trackMouse={event => (this.trackMouse(event))} /> ); } } App.propTypes = { angle: PropTypes.number.isRequired, moveObjects: PropTypes.func.isRequired, }; export default App;
, . :
componentDidMount
: ( lifecycle method ) ( setInterval ), moveObjects
;trackMouse
: canvasMousePosition
App
. moveObjects
. , HTML-. . canvasMousePosition
.render
: ( angle ) trackMouse
Canvas
. angle
trackMouse
SVG .App.propTypes
: : angle
moveObjects
. , . e, moveObjects â , .
App
formula.js
:
export const getCanvasPosition = (event) => {
, , StackOverflow .
, â Canvas
. Canvas.jsx
( ./src/components
) :
import React from 'react'; import PropTypes from 'prop-types'; import Sky from './Sky'; import Ground from './Ground'; import CannonBase from './CannonBase'; import CannonPipe from './CannonPipe'; const Canvas = (props) => { const viewBox = [window.innerWidth / -2, 100 - window.innerHeight, window.innerWidth, window.innerHeight]; return ( <svg id="aliens-go-home-canvas" preserveAspectRatio="xMaxYMax none" onMouseMove={props.trackMouse} viewBox={viewBox} > <Sky /> <Ground /> <CannonPipe rotation={props.angle} /> <CannonBase /> </svg> ); }; Canvas.propTypes = { angle: PropTypes.number.isRequired, trackMouse: PropTypes.func.isRequired, }; export default Canvas;
:
CannonPipe.rotation
: . Redux store ( (mapStateToProps mapDispatchToProps) connect, Game â . ).svg.onMouseMove
: , .Canvas.propTypes
: angle
trackMouse
.
! . npm start
( ). http://localhost:3000/ - . .
, ?!
, . create-react-app
, , . , . , .
, , . .
!
!
翻蚳è
ãã
, "" . ã©ãæããŸããïŒ