├── _config.yml ├── src ├── const │ └── PanoConst.js ├── effect │ ├── style │ │ ├── images │ │ │ ├── close.png │ │ │ ├── hands.png │ │ │ ├── phone.png │ │ │ ├── video-hands.png │ │ │ └── video-phone-gyro.png │ │ ├── EffectAlphaVideoPanel.less │ │ ├── EffectInfoCard.less │ │ ├── EffectControlPanel.less │ │ ├── EffectVideoPanel.less │ │ └── EffectImageCard.less │ ├── EffectControlPanel.jsx │ ├── EffectInfoCard.jsx │ ├── EffectImageCard.jsx │ ├── EffectVideoPanel.jsx │ ├── EffectContainer.jsx │ ├── EffectAlphaVideoPanle.jsx │ └── NewEffectAlphaVideoPanle.jsx ├── event │ ├── Events.js │ ├── EventCenter.js │ └── EventBus.js ├── reducer.js ├── setupTests.js ├── App.test.js ├── store.js ├── index.css ├── index.js ├── App.css ├── utils │ ├── osuitls.js │ └── fullscreen.js ├── display │ ├── loader │ │ ├── ObjLoader.js │ │ └── FBXLoader.js │ ├── ResourceBox │ │ ├── EmbeddedResource │ │ │ ├── EmbeddedImageBox.js │ │ │ ├── EmbeddedVideoBox.js │ │ │ ├── EmbeddedTextBox.js │ │ │ ├── EmbeddedBox.js │ │ │ └── EmbeddedBoxManager.js │ │ └── ResourceBoxHelper.js │ ├── CenterModelHelper.js │ ├── SpriteParticleHelper.js │ └── HotSpotHelper.js ├── action │ ├── CameraMoveAction.js │ └── ViewConvertHelper.js ├── redux │ └── player.redux.js ├── logo.svg ├── controls │ └── DeviceOrientationControls.js ├── Player.js ├── manager │ └── VRHelper.js └── texture │ └── TextureHelper.js ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── shuttle.mp4 ├── texture1.png ├── SambaDancing.fbx ├── hotspot_video.png ├── manifest.json ├── index.html └── mock │ └── view1.json ├── deploy ├── upload.sh └── nginx.conf ├── doc ├── imgs │ └── react-xrplayer-preview.png ├── feature-and-todo.md ├── dev.md └── api-doc.md ├── .npmignore ├── .babelrc ├── Dockerfile ├── config ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── pnpTs.js ├── getHttpsConfig.js ├── paths.js ├── env.js ├── modules.js └── webpackDevServer.config.js ├── example ├── src │ └── index.js └── index.html ├── .gitignore ├── LICENSE ├── jsdoc.json ├── scripts ├── test.js ├── start.js └── build.js ├── README.md ├── package.json └── .vscode └── reactjsx.code-snippets /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /src/const/PanoConst.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 全景球体的半径 3 | */ 4 | export const Radius = 500; -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/shuttle.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/public/shuttle.mp4 -------------------------------------------------------------------------------- /public/texture1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/public/texture1.png -------------------------------------------------------------------------------- /deploy/upload.sh: -------------------------------------------------------------------------------- 1 | # only runnable on ljc pc 2 | 3 | scp -r build aliyunecs:/production/front/youmu-xr-client -------------------------------------------------------------------------------- /public/SambaDancing.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/public/SambaDancing.fbx -------------------------------------------------------------------------------- /public/hotspot_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/public/hotspot_video.png -------------------------------------------------------------------------------- /doc/imgs/react-xrplayer-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/doc/imgs/react-xrplayer-preview.png -------------------------------------------------------------------------------- /src/effect/style/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/src/effect/style/images/close.png -------------------------------------------------------------------------------- /src/effect/style/images/hands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/src/effect/style/images/hands.png -------------------------------------------------------------------------------- /src/effect/style/images/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/src/effect/style/images/phone.png -------------------------------------------------------------------------------- /src/effect/style/images/video-hands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/src/effect/style/images/video-hands.png -------------------------------------------------------------------------------- /src/effect/style/images/video-phone-gyro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZWboy97/react-xrplayer/HEAD/src/effect/style/images/video-phone-gyro.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # .npmignore 2 | src 3 | .babelrc 4 | .gitignore 5 | deploy 6 | .vscode 7 | build 8 | config 9 | node_modules 10 | public 11 | scripts 12 | Dockerfile 13 | yarn.lock 14 | /jsdoc -------------------------------------------------------------------------------- /src/event/Events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 事件 3 | */ 4 | 5 | const EVENT_SENCE_RES_READY = 'event_sence_res_ready'; //全景背景资源准备完成 6 | 7 | export default { 8 | EVENT_SENCE_RES_READY 9 | }; 10 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-class-properties" 8 | ] 9 | } -------------------------------------------------------------------------------- /src/reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { player } from './redux/player.redux'; 3 | const rootReducer = combineReducers({ 4 | player 5 | }); 6 | export default rootReducer; -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # production environment 2 | FROM nginx 3 | COPY ./build /usr/share/nginx/html 4 | RUN rm /etc/nginx/conf.d/default.conf 5 | COPY deploy/nginx.conf /etc/nginx/conf.d 6 | EXPOSE 80 7 | CMD ["nginx", "-g", "daemon off;" ] -------------------------------------------------------------------------------- /src/event/EventCenter.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 尝试将事件处理统一管理起来 4 | */ 5 | class EventCenter { 6 | constructor() { 7 | this.init(); 8 | } 9 | 10 | init = () => { 11 | } 12 | 13 | } 14 | export default EventCenter; -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /deploy/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | location / { 5 | root /usr/share/nginx/html; 6 | index index.html index.htm; 7 | try_files $uri $uri/ /index.html; 8 | } 9 | 10 | error_page 500 502 503 504 /50x.html; 11 | 12 | location = /50x.html { 13 | root /usr/share/nginx/html; 14 | } 15 | } -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import thunk from 'redux-thunk'; // 中间件,用来处理异步数据 3 | import rootReducer from './reducer'; 4 | 5 | const store = createStore( 6 | rootReducer, 7 | compose( 8 | applyMiddleware(thunk), 9 | window.devToolsExtension ? window.devToolsExtension() : f => f 10 | ) 11 | ); 12 | export default store; 13 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './app'; 4 | 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root')); 9 | 10 | // If you want your app to work offline and load faster, you can change 11 | // unregister() to register() below. Note this comes with some pitfalls. 12 | // Learn more about service workers: https://bit.ly/CRA-PWA 13 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | #IDE 4 | .idea 5 | 6 | # dependencies 7 | /node_modules 8 | /.pnp 9 | .pnp.js 10 | 11 | # testing 12 | /coverage 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | yarn.lock 25 | package-lock.json 26 | /lib 27 | /build 28 | /jsdoc 29 | /public/res -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './index.css'; 3 | import Player from './Player'; 4 | import { Provider } from 'react-redux' // https://react-redux.js.org/api/connect 5 | import store from './store'; 6 | 7 | class XRPlayer extends React.Component { 8 | render() { 9 | return ( 10 | 11 | 12 | 13 | ) 14 | } 15 | } 16 | 17 | export default XRPlayer; 18 | 19 | -------------------------------------------------------------------------------- /src/effect/style/EffectAlphaVideoPanel.less: -------------------------------------------------------------------------------- 1 | .alpha_video_overlay { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | .grep_overlay { 12 | background-color: rgba(0, 0, 0, .5); 13 | } 14 | 15 | .close { 16 | position: absolute; 17 | z-index: 12; 18 | right: 1rem; 19 | top: 1rem; 20 | width: 2rem; 21 | cursor: pointer; 22 | height: 2rem; 23 | background: url(./images/close.png) 0 0 no-repeat; 24 | background-size: cover; 25 | } -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /config/pnpTs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { resolveModuleName } = require('ts-pnp'); 4 | 5 | exports.resolveModuleName = ( 6 | typescript, 7 | moduleName, 8 | containingFile, 9 | compilerOptions, 10 | resolutionHost 11 | ) => { 12 | return resolveModuleName( 13 | moduleName, 14 | containingFile, 15 | compilerOptions, 16 | resolutionHost, 17 | typescript.resolveModuleName 18 | ); 19 | }; 20 | 21 | exports.resolveTypeReferenceDirective = ( 22 | typescript, 23 | moduleName, 24 | containingFile, 25 | compilerOptions, 26 | resolutionHost 27 | ) => { 28 | return resolveModuleName( 29 | moduleName, 30 | containingFile, 31 | compilerOptions, 32 | resolutionHost, 33 | typescript.resolveTypeReferenceDirective 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/effect/style/EffectInfoCard.less: -------------------------------------------------------------------------------- 1 | .info-overlay { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | width: 100%; 8 | height: 100%; 9 | background-color: rgba(0, 0, 0, .5); 10 | } 11 | 12 | .iframe { 13 | min-width: 500px; 14 | width: 50%; 15 | position: absolute; 16 | top: 50%; 17 | left: 50%; 18 | height: 70%; 19 | z-index: 12; 20 | -webkit-transform: translateX(-50%) translateY(-50%); 21 | -ms-transform: translateX(-50%) translateY(-50%); 22 | transform: translateX(-50%) translateY(-50%); 23 | } 24 | 25 | .info-close { 26 | position: absolute; 27 | z-index: 12; 28 | right: 2rem; 29 | top: 2rem; 30 | width: 2rem; 31 | height: 2rem; 32 | background: url(./images/close.png) 0 0 no-repeat; 33 | background-size: cover; 34 | } -------------------------------------------------------------------------------- /src/effect/style/EffectControlPanel.less: -------------------------------------------------------------------------------- 1 | .control-overlay { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | background-color: rgba(0, 0, 0, .5); 8 | } 9 | 10 | .control-container { 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | width: 42rem; 15 | min-height: 20%; 16 | font-size: 1rem; 17 | background-color: rgba(0, 0, 0, .3); 18 | z-index: 12; 19 | visibility: visible; 20 | -webkit-transform: translateX(-50%) translateY(-50%); 21 | -ms-transform: translateX(-50%) translateY(-50%); 22 | transform: translateX(-50%) translateY(-50%); 23 | } 24 | 25 | 26 | .control-close { 27 | position: absolute; 28 | z-index: 12; 29 | right: 1rem; 30 | top: 1rem; 31 | width: 2rem; 32 | cursor: pointer; 33 | height: 2rem; 34 | background: url(./images/close.png) 0 0 no-repeat; 35 | background-size: cover; 36 | } -------------------------------------------------------------------------------- /src/effect/style/EffectVideoPanel.less: -------------------------------------------------------------------------------- 1 | .video-overlay { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | width: 100%; 8 | height: 100%; 9 | background-color: rgba(0, 0, 0, .5); 10 | } 11 | 12 | .container { 13 | 14 | .video { 15 | position: absolute; 16 | top: 50%; 17 | left: 50%; 18 | width: 60%; 19 | max-height: 100%; 20 | z-index: 12; 21 | -webkit-transform: translateX(-50%) translateY(-50%); 22 | -ms-transform: translateX(-50%) translateY(-50%); 23 | transform: translateX(-50%) translateY(-50%); 24 | } 25 | } 26 | 27 | .content { 28 | margin: auto; 29 | } 30 | 31 | .video-close { 32 | position: absolute; 33 | z-index: 12; 34 | right: 1rem; 35 | top: 1rem; 36 | width: 2rem; 37 | cursor: pointer; 38 | height: 2rem; 39 | background: url(./images/close.png) 0 0 no-repeat; 40 | background-size: cover; 41 | } -------------------------------------------------------------------------------- /src/utils/osuitls.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-useless-escape */ 2 | export var OS = { 3 | weixin: navigator.userAgent.indexOf('MicroMessenger') > -1, 4 | android: /android/i.test(navigator.userAgent.toLowerCase()), 5 | ios: /(iphone|ipad|ipod|ios)/i.test(navigator.userAgent.toLowerCase()), 6 | googlePixel: navigator.userAgent.match(/;\sPixel\sBuild\//), 7 | MiOS: navigator.userAgent.match(/;\sMI\s\d\sBuild\//), 8 | samsungOS: navigator.userAgent.match(/;\sSM\-\w+\sBuild\//), 9 | isGooglePixel: function () { 10 | return this.googlePixel != null; 11 | }, 12 | isMiOS: function () { 13 | return this.MiOS != null; 14 | }, 15 | isSamsung: function () { 16 | return this.samsungOS != null; 17 | }, 18 | isMobile: function () { 19 | return this.android || this.ios; 20 | }, 21 | 22 | isAndroid: function () { 23 | return this.android; 24 | }, 25 | 26 | isiOS: function () { 27 | return this.ios; 28 | }, 29 | 30 | isWeixin: function () { 31 | return this.weixin; 32 | } 33 | } -------------------------------------------------------------------------------- /src/effect/EffectControlPanel.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import './style/EffectControlPanel.less'; 4 | 5 | class EffectControlPanel extends Component { 6 | 7 | componentDidMount() { 8 | } 9 | 10 | onCloseClickListener = (e) => { 11 | e.preventDefault(); 12 | if (this.props.onCloseClickHandler) { 13 | this.props.onCloseClickHandler(); 14 | } 15 | } 16 | 17 | componentWillUnmount() { 18 | } 19 | 20 | render() { 21 | return ( 22 |
23 |
24 | 25 | 26 |
27 |
31 |
32 | ) 33 | } 34 | } 35 | 36 | EffectControlPanel.propTypes = { 37 | onCloseClickHandler: PropTypes.func.isRequired, 38 | }; 39 | 40 | export default EffectControlPanel; 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ZWboy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/effect/style/EffectImageCard.less: -------------------------------------------------------------------------------- 1 | .img-overlay { 2 | position: absolute; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | width: 100%; 8 | height: 100%; 9 | background-color: rgba(0, 0, 0, .5); 10 | } 11 | 12 | .container { 13 | .image { 14 | cursor: pointer; 15 | position: absolute; 16 | top: 50%; 17 | left: 50%; 18 | width: 60%; 19 | max-height: 100%; 20 | z-index: 12; 21 | -webkit-transform: translateX(-50%) translateY(-50%); 22 | -ms-transform: translateX(-50%) translateY(-50%); 23 | transform: translateX(-50%) translateY(-50%); 24 | // -webkit-transform: translateX(-50%) translateY(-50%); 25 | // -ms-transform: translateX(-50%) translateY(-50%); 26 | // transform: translateX(-50%) translateY(-50%); 27 | } 28 | } 29 | 30 | .img-close { 31 | position: absolute; 32 | z-index: 12; 33 | right: 1rem; 34 | top: 1rem; 35 | width: 2rem; 36 | cursor: pointer; 37 | height: 2rem; 38 | background: url(./images/close.png) 0 0 no-repeat; 39 | background-size: cover; 40 | } -------------------------------------------------------------------------------- /src/effect/EffectInfoCard.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Iframe from 'react-iframe' 4 | import './style/EffectInfoCard.less'; 5 | 6 | class EffectInfoCard extends Component { 7 | 8 | onCloseClickListener = (e) => { 9 | e.preventDefault(); 10 | if (this.props.onCloseClickHandler) { 11 | this.props.onCloseClickHandler(); 12 | } 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |