├── src
├── entry.js
├── scss
│ ├── poster.scss
│ ├── theme
│ │ └── _color.scss
│ ├── animation
│ │ ├── _selector.scss
│ │ └── _loading.scss
│ ├── base
│ │ └── _base.scss
│ └── components
│ │ ├── _loading.scss
│ │ ├── _editor.scss
│ │ ├── _selector.scss
│ │ └── _result.scss
└── js
│ ├── loading.js
│ ├── config.js
│ ├── poster.js
│ ├── selector.js
│ └── editor.js
├── dist
└── poster
│ ├── qr.jpg
│ ├── item.png
│ ├── footer.png
│ ├── qr-stuq.jpg
│ ├── title-1.png
│ ├── title-2.png
│ ├── characters
│ ├── 01.png
│ ├── 02.png
│ ├── 03.png
│ ├── 04.png
│ ├── 05.png
│ ├── 06.png
│ ├── 07.png
│ ├── 08.png
│ ├── 09.png
│ ├── 10.png
│ ├── 11.png
│ ├── 12.png
│ ├── 13.png
│ ├── 14.png
│ ├── 15.png
│ ├── 16.png
│ └── 17.png
│ └── icons.svg
├── screenshoot
├── poster-1.png
├── poster-2.png
├── poster-3.png
├── poster-4.png
└── poster-5.png
├── jade
├── index.jade
└── detail.jade
├── index.html
├── .gitignore
├── package.json
├── LICENSE
├── webpack.config.js
├── README.md
└── detail.html
/src/entry.js:
--------------------------------------------------------------------------------
1 | require('./scss/poster.scss');
2 | require('./js/poster.js');
--------------------------------------------------------------------------------
/dist/poster/qr.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/qr.jpg
--------------------------------------------------------------------------------
/dist/poster/item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/item.png
--------------------------------------------------------------------------------
/dist/poster/footer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/footer.png
--------------------------------------------------------------------------------
/dist/poster/qr-stuq.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/qr-stuq.jpg
--------------------------------------------------------------------------------
/dist/poster/title-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/title-1.png
--------------------------------------------------------------------------------
/dist/poster/title-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/title-2.png
--------------------------------------------------------------------------------
/screenshoot/poster-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/screenshoot/poster-1.png
--------------------------------------------------------------------------------
/screenshoot/poster-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/screenshoot/poster-2.png
--------------------------------------------------------------------------------
/screenshoot/poster-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/screenshoot/poster-3.png
--------------------------------------------------------------------------------
/screenshoot/poster-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/screenshoot/poster-4.png
--------------------------------------------------------------------------------
/screenshoot/poster-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/screenshoot/poster-5.png
--------------------------------------------------------------------------------
/dist/poster/characters/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/01.png
--------------------------------------------------------------------------------
/dist/poster/characters/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/02.png
--------------------------------------------------------------------------------
/dist/poster/characters/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/03.png
--------------------------------------------------------------------------------
/dist/poster/characters/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/04.png
--------------------------------------------------------------------------------
/dist/poster/characters/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/05.png
--------------------------------------------------------------------------------
/dist/poster/characters/06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/06.png
--------------------------------------------------------------------------------
/dist/poster/characters/07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/07.png
--------------------------------------------------------------------------------
/dist/poster/characters/08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/08.png
--------------------------------------------------------------------------------
/dist/poster/characters/09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/09.png
--------------------------------------------------------------------------------
/dist/poster/characters/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/10.png
--------------------------------------------------------------------------------
/dist/poster/characters/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/11.png
--------------------------------------------------------------------------------
/dist/poster/characters/12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/12.png
--------------------------------------------------------------------------------
/dist/poster/characters/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/13.png
--------------------------------------------------------------------------------
/dist/poster/characters/14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/14.png
--------------------------------------------------------------------------------
/dist/poster/characters/15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/15.png
--------------------------------------------------------------------------------
/dist/poster/characters/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/16.png
--------------------------------------------------------------------------------
/dist/poster/characters/17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeamStuQ/Poster/HEAD/dist/poster/characters/17.png
--------------------------------------------------------------------------------
/src/scss/poster.scss:
--------------------------------------------------------------------------------
1 | @charset 'utf-8';
2 |
3 | // Theme
4 | @import 'theme/color';
5 |
6 | // Base
7 | @import 'base/base';
8 |
9 | // Animation
10 | @import 'animation/loading';
11 | @import 'animation/selector';
12 |
13 | // Components
14 | @import 'components/loading';
15 | @import 'components/selector';
16 | @import 'components/editor';
17 | @import 'components/result';
--------------------------------------------------------------------------------
/src/scss/theme/_color.scss:
--------------------------------------------------------------------------------
1 | // color.scss
2 |
3 | $white: #fff;
4 | $black: #000;
5 | $blue: #245c9f;
6 | $blue-a: rgba(36, 92, 159, 1);
7 | $blue-n: rgba(36, 92, 159, 0);
8 | $dgray: #727171;
9 | $lgray: #e5e5e5;
10 |
11 | $bar-color: #008cd6;
12 | $ball-color: #00b7ed;
13 |
14 | $sblue: #eff5fc;
15 | $syellow: #fef9e8;
16 | $sgreen: #ebf6f5;
17 | $spink: #fdf2f8;
18 |
19 | $border-gray: #b4b5b5;
--------------------------------------------------------------------------------
/jade/index.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html
3 | head
4 | meta(charset='utf-8')
5 | meta(name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densitydpi=device-dpi')
6 | title 海报生成器
7 | body
8 | div.bg
9 | svg
10 | use(xlink:href='/dist/poster/icons.svg#as-mountain')
11 | #poster
12 | script(src='/dist/poster.min.js')
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 海报生成器
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/scss/animation/_selector.scss:
--------------------------------------------------------------------------------
1 | @keyframes slideUp {
2 | 0% {
3 | transform: translateY(12px);
4 | }
5 | 10% {
6 | transform: translateY(10px);
7 | }
8 | 20% {
9 | transform: translateY(8px);
10 | }
11 | 30% {
12 | transform: translateY(6px);
13 | }
14 | 40% {
15 | transform: translateY(4px);
16 | }
17 | 50% {
18 | transform: translateY(2px);
19 | }
20 | 60% {
21 | transform: translateY(-2px);
22 | }
23 | 70% {
24 | transform: translateY(-4px);
25 | }
26 | 80% {
27 | transform: translateY(-6px);
28 | }
29 | 90% {
30 | transform: translateY(-8px);
31 | }
32 | 100% {
33 | transform: translateY(-10px);
34 | }
35 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
--------------------------------------------------------------------------------
/src/js/loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class extends React.Component {
4 | render() {
5 | return (
6 |
7 |
8 | - 每一位程序员
9 | - 都有专属自己的套路
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 | );
24 | }
25 | }
--------------------------------------------------------------------------------
/src/scss/base/_base.scss:
--------------------------------------------------------------------------------
1 | // base.scss
2 |
3 | * {
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | display: block;
9 | position: relative;
10 | width: 100%;
11 | margin: 0;
12 | padding: 0;
13 | font-family: "Helvetica Neue", Helvetica, Arial, "Lantinghei SC", "Hiragino Sans GB", STHeiti, "Microsoft Yahei", "Microsoft Sans Serif", sans-serif;
14 | font-size: 16px;
15 | overflow: hidden;
16 | }
17 |
18 | .poster {
19 | display: block;
20 | position: relative;
21 | width: 100%;
22 | }
23 |
24 | .bg {
25 | display: block;
26 | position: absolute;
27 | width: 100%;
28 | height: 100vh;
29 | top: 0;
30 | z-index: -1;
31 | opacity: 0.1;
32 | overflow: hidden;
33 |
34 | svg {
35 | position: absolute;
36 | bottom: 0;
37 | left: -50%;
38 | width: 300vw;
39 | height: 124vw;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Poster",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "entry.js",
6 | "dependencies": {
7 | "babel-core": "^6.9.1",
8 | "babel-loader": "^6.2.4",
9 | "babel-preset-es2015": "^6.9.0",
10 | "babel-preset-react": "^6.5.0",
11 | "css-loader": "^0.23.1",
12 | "jquery": "^3.0.0",
13 | "node-sass": "^3.7.0",
14 | "postcss": "^5.0.21",
15 | "postcss-loader": "^0.9.1",
16 | "precss": "^1.4.0",
17 | "react": "^15.1.0",
18 | "react-dom": "^15.1.0",
19 | "sass-loader": "^3.2.0",
20 | "style-loader": "^0.13.1",
21 | "webpack": "^1.13.1"
22 | },
23 | "devDependencies": {
24 | "fullpage.js": "^2.8.1",
25 | "swiper": "^3.3.1"
26 | },
27 | "scripts": {
28 | "test": "echo \"Error: no test specified\" && exit 1"
29 | },
30 | "author": "Steve Zhang",
31 | "license": "MIT"
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (C) 2016 Steve Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack"),
2 | autoprefixer = require('autoprefixer'),
3 | precss = require('precss');
4 |
5 | module.exports = {
6 | context: __dirname,
7 |
8 | entry: './src/entry',
9 |
10 | output: {
11 | path: './dist',
12 | filename: 'poster.min.js'
13 | },
14 |
15 | devtool: 'source-map',
16 |
17 | module: {
18 | loaders: [
19 | {
20 | test: /\.scss$/,
21 | loaders: [
22 | 'style',
23 | 'css?sourceMap',
24 | 'postcss-loader',
25 | 'sass?sourceMap'
26 | ]
27 | },
28 |
29 | {
30 | test: /\.css$/,
31 | loaders: [
32 | 'style',
33 | 'css?sourceMap',
34 | 'postcss-loader'
35 | ]
36 | },
37 |
38 | {
39 | test: /\.js?$/,
40 | exclude: /(node_modules|bower_components)/,
41 | loader: 'babel',
42 | query: {
43 | presets: [
44 | 'react',
45 | 'es2015'
46 | ]
47 | }
48 | }
49 | ]
50 | },
51 |
52 | postcss: () => {
53 | return [autoprefixer, precss];
54 | },
55 |
56 | plugins: [
57 | new webpack.optimize.UglifyJsPlugin({
58 | compress: {
59 | warnings: false
60 | }
61 | })
62 | ]
63 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 海报生成器
2 |
3 | 使用 <canvas> 的**[海报生成器](http://www.geekbang.org/poster)**。
4 |
5 | *注:页面主要为手机端设计,在电脑浏览器中打开时,请缩小浏览器宽高比至手机竖屏比例。*
6 |
7 | ## 构建
8 |
9 | React + ES6 + SCSS + Webpack
10 |
11 | ## 使用
12 | 1.克隆项目至本地环境并进入项目目录:
13 |
14 | ```sh
15 | git clone https://github.com/TeamStuQ/Poster
16 | cd Poster
17 | ```
18 |
19 | 2.安装相关的依赖包(请确保你已经安装了[node.js](https://nodej.org/)):
20 |
21 | ```sh
22 | npm install
23 | ```
24 |
25 | 3.如果需要,在全局环境下安装 [webpack](https://github.com/webpak/webpack):
26 |
27 | ```sh
28 | npm install wepack -g
29 | ```
30 |
31 | 4.将项目的源代码(位于 src/ 下)编译至生产所需的目标码(位于 dist/ 下):
32 |
33 | ```sh
34 | webpack -w
35 | ```
36 |
37 | webpack 使用说明请参考 [https://webpack.github.io/docs/](https://webpack.gihub.io/docs/)。
38 |
39 | 5.效果预览,请在项目的根目录下开启 http 服务器,推荐使用 [webpack-dev-server](https://github.com/webpack/wbpack-dev-server)
40 |
41 | ```sh
42 | npm install webpack-dev-server -g
43 | webpck-dev-server
44 | ```
45 |
46 | 然后在浏览器中访问 [http://localhost:8080/](http://localhost:8080/) 即可。
47 |
48 | **注:本项目不包含后端代码及其实现。**
49 |
50 | ## 截图
51 |
52 | 
53 |
54 | 
55 |
56 | 
57 |
58 | 
59 |
60 | 
61 |
62 | ## 其他
63 |
64 | 本项目中使用到的其他开源项目:
65 |
66 | * [fullpage.js](https://github.com/alvarotrigo/fullPage.js/)
67 |
68 | * [swiper](https://github.com/nolimits4web/swiper/)
69 |
70 | * [CSS 动画](https://codepen.io/ispal/pen/mVaaJe)
71 |
--------------------------------------------------------------------------------
/src/scss/components/_loading.scss:
--------------------------------------------------------------------------------
1 | // _loading.scss
2 |
3 | .loading {
4 | display: block;
5 | position: relative;
6 | width: 100%;
7 | height: 100vh;
8 | padding-top: 20vh;
9 |
10 | ul {
11 | display: block;
12 | position: relative;
13 | width: 100%;
14 | margin: 0;
15 | padding: 0;
16 | list-style: none;
17 |
18 | li {
19 | display: block;
20 | position: relative;
21 | width: 100%;
22 | text-align: center;
23 | font-size: 1rem;
24 | font-weight: 400;
25 | line-height: 1em;
26 | margin-bottom: 0.75rem;
27 | color: $dgray;
28 | }
29 | }
30 |
31 | .loader {
32 | display: flex;
33 | position: relative;
34 | margin: -2rem auto 0;
35 | align-items: center;
36 | justify-content: center;
37 | position: relative;
38 | width: 75px;
39 | height: 100px;
40 | transform: scale(0.5);
41 |
42 | &__bar {
43 | position: absolute;
44 | bottom: 0;
45 | width: 10px;
46 | height: 50%;
47 | background: $bar-color;
48 | transform-origin: center bottom;
49 | box-shadow: 1px 1px 0 rgba(0,0,0,.2);
50 |
51 | @for $i from 1 through 5 {
52 | &:nth-child(#{$i}) {
53 | left: ($i - 1) * 15px;
54 | transform: scale(1,$i*.2);
55 | animation: barUp#{$i} 4s infinite;
56 | }
57 | }
58 |
59 | }
60 |
61 | &__ball {
62 | position: absolute;
63 | bottom: 10px;
64 | left: 0;
65 | width: 10px;
66 | height: 10px;
67 | background: $ball-color;
68 | border-radius: 50%;
69 | animation: ball 4s infinite;
70 | }
71 | }
72 |
73 | .as-logo {
74 | display: block;
75 | position: absolute;
76 | width: 30vw;
77 | height: 9.3vw;
78 | bottom: 12vh;
79 | left: 35vw;
80 | }
81 | }
--------------------------------------------------------------------------------
/jade/detail.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html
3 | head
4 | meta(charset='utf-8')
5 | meta(name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densitydpi=device-dpi')
6 | title 我是{{ poster.data.position }}!| {{ poster.data.slogan }}
7 | body
8 | div.result
9 | div.section.show
10 | img.photo(alt='photo' src='{{ poster.image }}' id='photo')
11 | div.choose
12 | a#download 下载
13 | a#new 创建我的专属套路
14 | a#share 分享
15 | div.next
16 | span 更多套路
17 | div.next-slide
18 | svg
19 | use(xlink:href='/dist/poster/icons.svg#up')
20 | svg
21 | use(xlink:href='/dist/poster/icons.svg#up')
22 | div.section.more
23 | div.to To You:
24 | ul.sentences
25 | li 长按识别图中二维码
26 | li 关注<ArchSummit技术关注>
27 | li 回复“套路”
28 | li 看看其他程序员隐藏的文艺之力!
29 | div.from From 官方套路
30 | img.qr(alt='qrcode' src='/dist/poster/qr.jpg')
31 | div.next
32 | span 下方高能
33 | div.next-slide
34 | svg
35 | use(xlink:href='/dist/poster/icons.svg#up')
36 | svg
37 | use(xlink:href='/dist/poster/icons.svg#up')
38 | div.section.more
39 | div.to 哇,你发现了这个彩蛋:
40 | ul.sentences
41 | li 长按识别图中二维码
42 | li 关注<StuQ订阅号>
43 | li 获得全套“StuQ职业技术角色技能图谱”
44 | li 还有,你一定会想要的“StuQ课程优惠码”
45 | div.from From stuq.org(读音:斯达Q)
46 | img.qr(alt='qrcode' src='/dist/poster/qr-stuq.jpg')
47 | div.download-info
48 | div.close
49 | svg
50 | use(xlink:href='/dist/poster/icons.svg#download')
51 | div.info 长按保存图片
52 | div.info 让大家感受你的文艺之力吧
53 | div.share-info
54 | div.close
55 | svg
56 | use(xlink:href='/dist/poster/icons.svg#share')
57 | div.info 点击右上角分享
58 | div.info 发现程序员朋友的另一面
59 | div.bg
60 | svg
61 | use(xlink:href='/dist/poster/icons.svg#as-mountain')
62 | script(src='/dist/poster.min.js')
--------------------------------------------------------------------------------
/src/scss/animation/_loading.scss:
--------------------------------------------------------------------------------
1 | @keyframes ball {
2 | 0% {
3 | transform: translate(0, 0);
4 | }
5 | 5% {
6 | transform: translate(8px, -14px);
7 | }
8 | 10% {
9 | transform: translate(15px, -10px);
10 | }
11 | 17% {
12 | transform: translate(23px, -24px);
13 | }
14 | 20% {
15 | transform: translate(30px, -20px);
16 | }
17 | 27% {
18 | transform: translate(38px, -34px);
19 | }
20 | 30% {
21 | transform: translate(45px, -30px)
22 | }
23 | 37% {
24 | transform: translate(53px, -44px)
25 | }
26 | 40% {
27 | transform: translate(60px, -40px)
28 | }
29 | 50% {
30 | transform: translate(60px, 0)
31 | }
32 | 57% {
33 | transform: translate(53px, -14px)
34 | }
35 | 60% {
36 | transform: translate(45px, -10px)
37 | }
38 | 67% {
39 | transform: translate(37px, -24px)
40 | }
41 | 70% {
42 | transform: translate(30px, -20px)
43 | }
44 | 77% {
45 | transform: translate(22px, -34px)
46 | }
47 | 80% {
48 | transform: translate(15px, -30px)
49 | }
50 | 87% {
51 | transform: translate(7px, -44px)
52 | }
53 | 90% {
54 | transform: translate(0, -40px)
55 | }
56 | 100% {
57 | transform: translate(0, 0);
58 | }
59 | }
60 |
61 | @keyframes barUp1 {
62 | 0% {
63 | transform: scale(1, .2);
64 | }
65 | 40%{
66 | transform: scale(1, .2);
67 | }
68 | 50% {
69 | transform: scale(1, 1);
70 | }
71 | 90% {
72 | transform: scale(1,1);
73 | }
74 | 100% {
75 | transform: scale(1,.2);
76 | }
77 | }
78 | @keyframes barUp2 {
79 | 0% {
80 | transform: scale(1, .4);
81 | }
82 | 40% {
83 | transform: scale(1, .4);
84 | }
85 | 50% {
86 | transform: scale(1, .8);
87 | }
88 | 90% {
89 | transform: scale(1, .8);
90 | }
91 | 100% {
92 | transform: scale(1, .4);
93 | }
94 | }
95 | @keyframes barUp3 {
96 | 0% {
97 | transform: scale(1, .6);
98 | }
99 | 100% {
100 | transform: scale(1, .6);
101 | }
102 | }
103 | @keyframes barUp4 {
104 | 0% {
105 | transform: scale(1, .8);
106 | }
107 | 40% {
108 | transform: scale(1, .8);
109 | }
110 | 50% {
111 | transform: scale(1, .4);
112 | }
113 | 90% {
114 | transform: scale(1, .4);
115 | }
116 | 100% {
117 | transform: scale(1, .8);
118 | }
119 | }
120 | @keyframes barUp5 {
121 | 0% {
122 | transform: scale(1, 1);
123 | }
124 | 40% {
125 | transform: scale(1, 1);
126 | }
127 | 50% {
128 | transform: scale(1, .2);
129 | }
130 | 90% {
131 | transform: scale(1, .2);
132 | }
133 | 100% {
134 | transform: scale(1, 1);
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/detail.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 我是{{ poster.data.position }}!| {{ poster.data.slogan }}
7 |
8 |
9 |
10 |

11 |
12 |
更多套路
13 |
14 |
17 |
20 |
21 |
22 |
23 |
24 |
To You:
25 |
26 | - 长按识别图中二维码
27 | - 关注<ArchSummit技术关注>
28 | - 回复“套路”
29 | - 看看其他程序员隐藏的文艺之力!
30 |
31 |
From 官方套路

32 |
下方高能
33 |
34 |
37 |
40 |
41 |
42 |
43 |
44 |
哇,你发现了这个彩蛋:
45 |
46 | - 长按识别图中二维码
47 | - 关注<StuQ订阅号>
48 | - 获得全套“StuQ职业技术角色技能图谱”
49 | - 还有,你一定会想要的“StuQ课程优惠码”
50 |
51 |
From stuq.org(读音:斯达Q)

52 |
53 |
54 |
55 |
56 |
59 |
长按保存图片
60 |
让大家感受你的文艺之力吧
61 |
62 |
63 |
64 |
67 |
点击右上角分享
68 |
发现程序员朋友的另一面
69 |
70 |
71 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/src/js/config.js:
--------------------------------------------------------------------------------
1 | export const posLimit = 7;
2 |
3 | export const sloganLimit = 16;
4 |
5 | export const posterSize = {
6 | 'width': 750,
7 | 'height': 1334,
8 | 'mainHe': 1151
9 | };
10 |
11 | export const photoSize = {
12 | 'width': 750,
13 | 'height': 659
14 | };
15 |
16 | export const photoTitle1 = '/dist/poster/title-1.png';
17 |
18 | export const photoTitle2 = '/dist/poster/title-2.png';
19 |
20 | export const sloganArrow = '/dist/poster/item.png';
21 |
22 | export const footer = '/dist/poster/footer.png';
23 |
24 | export const diyName = '???';
25 |
26 | export const themeColors = {
27 | 'white' : '#ffffff',
28 | 'blue' : '#eff5fc',
29 | 'green' : '#ebf6f5',
30 | 'yellow': '#fef9e8',
31 | 'pink' : '#fdf2f8'
32 | };
33 |
34 | export const imgData = [
35 | {
36 | name : '架构师',
37 | src : '/dist/poster/characters/01.png',
38 | slogan1: '有问题?重构!',
39 | slogan2: '不可能出现这种情况的啊?!',
40 | slogan3: '一定不会是我的原因,可能是硬件问题?'
41 | },
42 | {
43 | name : '前端工程师',
44 | src : '/dist/poster/characters/02.png',
45 | slogan1: '这不是bug,肯定是网络问题',
46 | slogan2: '你忘记清除缓存了,兄弟',
47 | slogan3: '兼容IE6?'
48 | },
49 | {
50 | name : 'PHP工程师',
51 | src : '/dist/poster/characters/03.png',
52 | slogan1: '这是字符编码的问题',
53 | slogan2: '等我下班后再跟你细说…',
54 | slogan3: '我一定要说服他们,PHP是最好的语言'
55 | },
56 | {
57 | name : '测试工程师',
58 | src : '/dist/poster/characters/04.png',
59 | slogan1: '我会在代码更替的时候添加单元测试',
60 | slogan2: '我检查过一遍了,别怕,上线吧!',
61 | slogan3: '我又不能测试所有的功能'
62 | },
63 | {
64 | name : '安卓工程师',
65 | src : '/dist/poster/characters/05.png',
66 | slogan1: '是不是你硬件出错了',
67 | slogan2: '老板,我们要买新的Android 手机',
68 | slogan3: '昨天明明正常,不知道为啥现在就不行了'
69 | },
70 | {
71 | name : 'iOS工程师',
72 | src : '/dist/poster/characters/06.png',
73 | slogan1: 'WWDC: World Wide Developer Crying',
74 | slogan2: '我的App评级为17+岁,你呢?',
75 | slogan3: '你是用黑苹果写的吧?'
76 | },
77 | {
78 | name : '运维工程师',
79 | src : '/dist/poster/characters/07.png',
80 | slogan1: '我们的预算还够吧?',
81 | slogan2: '我不是网管,也不是修电脑的!',
82 | slogan3: '入门运维工程狮最重要的要求是体力充沛'
83 | },
84 | {
85 | name : '安全工程师',
86 | src : '/dist/poster/characters/08.png',
87 | slogan1: '你是不是改了什么,你重演一下我看看',
88 | slogan2: '之前一直都没有出现过这种情况啊',
89 | slogan3: '我真不会写外挂呀'
90 | },
91 | {
92 | name : '全栈工程师',
93 | src : '/dist/poster/characters/09.png',
94 | slogan1: '只是改一行代码,不会对程序造成影响的!',
95 | slogan2: '这些代码是上一个开发者写的,不是我写的',
96 | slogan3: '没办法,这是一个公认的bug'
97 | },
98 | {
99 | name : '大数据工程师',
100 | src : '/dist/poster/characters/10.png',
101 | slogan1: '我们不考虑个例',
102 | slogan2: '快了,数据已经跑了90%了',
103 | slogan3: '大数据显示:我们组男女比例是40比0'
104 | },
105 | {
106 | name : '云计算工程师',
107 | src : '/dist/poster/characters/11.png',
108 | slogan1: '不用担心,这次肯定不会有问题了',
109 | slogan2: '我觉得写的很清楚啊,为什么你说看不懂',
110 | slogan3: '只改一行代码,不会造成影响的,放心'
111 | },
112 | {
113 | name : '机器学习工程师',
114 | src : '/dist/poster/characters/12.png',
115 | slogan1: '不就是算法吗,坐下来我给你讲',
116 | slogan2: '已经做好了,但还有一些细节要调一下',
117 | slogan3: '这个很简单的,我一个礼拜就能完成了'
118 | },
119 | {
120 | name : '嵌入式工程师',
121 | src : '/dist/poster/characters/13.png',
122 | slogan1: '这个任务很容易啊!',
123 | slogan2: '再给我两天,保证能做好',
124 | slogan3: '这只是暂时的方案,在正式版我会修改的'
125 | },
126 | {
127 | name : '智能硬件工程师',
128 | src : '/dist/poster/characters/14.png',
129 | slogan1: '我以后会补上注释和文档的',
130 | slogan2: '在我机器上好好的啊!',
131 | slogan3: '第一次出现这样的问题啊,我都没见过'
132 | },
133 | {
134 | name : 'CTO',
135 | src : '/dist/poster/characters/15.png',
136 | slogan1: '这个功能我们在下个版本修正',
137 | slogan2: '我们开会review代码吧',
138 | slogan3: '我们缺人啊!'
139 | },
140 | {
141 | name : '产品经理',
142 | src : '/dist/poster/characters/16.png',
143 | slogan1: '这是老板的需求',
144 | slogan2: '这是总监的需求',
145 | slogan3: '反正这不是我的需求'
146 | },
147 | {
148 | name : '设计师',
149 | src : '/dist/poster/characters/17.png',
150 | slogan1: '什么叫大气?',
151 | slogan2: '什么叫大气…',
152 | slogan3: '什么叫大气!'
153 | }
154 | ];
155 |
--------------------------------------------------------------------------------
/src/scss/components/_editor.scss:
--------------------------------------------------------------------------------
1 | .editor {
2 | display: block;
3 | position: relative;
4 | width: 100%;
5 | height: 100vh;
6 | padding: 5vh 6.4vw;
7 | z-index: 10;
8 |
9 | .poster-bg {
10 | display: none;
11 | position: absolute;
12 | top: 0;
13 | left: 0;
14 | width: 100vw;
15 | height: 100vh;
16 | background-color: rgba(0, 0, 0, 0.3);
17 | z-index: 10;
18 |
19 | &.active {
20 | display: block;
21 | }
22 | }
23 |
24 | .title {
25 | display: block;
26 | position: relative;
27 | width: 100%;
28 | text-align: center;
29 | font: {
30 | size: 5.6vw;
31 | weight: 400;
32 | }
33 | color: $black;
34 | line-height: 1em;
35 | margin-bottom: 8vh;
36 | }
37 |
38 | .limit {
39 | display: block;
40 | position: relative;
41 | width: 100%;
42 | text-align: left;
43 | font: {
44 | size: 3.2vw;
45 | weight: 300;
46 | }
47 | color: $dgray;
48 | line-height: 1em;
49 | margin-bottom: 2vw;
50 | }
51 |
52 | .slogans {
53 | display: block;
54 | position: relative;
55 | width: 100%;
56 | margin: 0 {
57 | bottom: 10.5vh;
58 | }
59 | padding: 0;
60 | list-style: none;
61 |
62 | li {
63 | display: block;
64 | position: relative;
65 | width: 100%;
66 | margin-bottom: 1.5vw;
67 |
68 | &:last-child {
69 | margin-bottom: 0;
70 | }
71 |
72 | input {
73 | display: block;
74 | position: relative;
75 | width: 100%;
76 | height: 12.4vw;
77 | line-height: 12.4vw;
78 | padding: 0 3.6vw;
79 | font: {
80 | size: 4vw;
81 | weight: 400;
82 | }
83 | color: $black;
84 | border: 1px solid $border-gray;
85 | }
86 | }
87 | }
88 |
89 | .colors {
90 | display: flex;
91 | position: relative;
92 | width: 100%;
93 | margin: 0 {
94 | top: 3.5vw;
95 | bottom: 9vw;
96 | }
97 | padding: 0;
98 | list-style: none;
99 | flex {
100 | direction: row;
101 | wrap: wrap;
102 | }
103 | justify-content: space-around;
104 | align-items: stretch;
105 |
106 | li {
107 | display: block;
108 | position: relative;
109 | width: 7.6vw;
110 | height: 7.6vw;
111 | padding: 2vw;
112 | border: 1px solid $border-gray {
113 | radius: 50%;
114 | }
115 | cursor: pointer;
116 |
117 | &.white {
118 | background-color: $white;
119 | }
120 |
121 | &.blue {
122 | background-color: $sblue;
123 | }
124 |
125 | &.green {
126 | background-color: $sgreen;
127 | }
128 |
129 | &.yellow {
130 | background-color: $syellow;
131 | }
132 |
133 | &.pink {
134 | background-color: $spink;
135 | }
136 |
137 | &.active {
138 | cursor: default;
139 |
140 | &:after {
141 | content: '';
142 | display: block;
143 | position: relative;
144 | width: 100%;
145 | height: 100%;
146 | border: 1px solid $border-gray {
147 | radius: 50%;
148 | }
149 | background-color: $white;
150 | box-sizing: border-box;
151 | }
152 | }
153 | }
154 | }
155 |
156 | .gen {
157 | display: flex;
158 | position: relative;
159 | width: 100%;
160 | margin: 0;
161 | padding: 0;
162 | list-style: none;
163 | flex {
164 | direction: row;
165 | wrap: wrap;
166 | }
167 | justify-content: space-around;
168 | align-items: stretch;
169 |
170 | li {
171 | display: inline-block;
172 | position: relative;
173 | width: 40vw;
174 | height: 12vw;
175 | line-height: 12vw;
176 | text-align: center;
177 | border-radius: 10px;
178 | color: $white;
179 | font: {
180 | size: 4.4vw;
181 | weight: 400;
182 | }
183 | cursor: pointer;
184 | letter-spacing: 0.5em;
185 |
186 | &:first-child {
187 | background-color: $blue;
188 | }
189 |
190 | &:last-child {
191 | background-color: $dgray;
192 | }
193 |
194 | &.forbidden {
195 | display: none;
196 | }
197 | }
198 | }
199 |
200 | .poster-canvas {
201 | display: none;
202 | position: absolute;
203 | width: 80vw;
204 | top: 5vw;
205 | left: 10vw;
206 | z-index: 11;
207 |
208 | &.active {
209 | display: block;
210 | }
211 | }
212 | }
--------------------------------------------------------------------------------
/src/js/poster.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import fullpage from 'fullpage.js';
5 |
6 | import {
7 | posLimit,
8 | sloganLimit,
9 | posterSize,
10 | photoSize,
11 | photoTitle1,
12 | photoTitle2,
13 | sloganArrow,
14 | footer,
15 | diyName,
16 | themeColors,
17 | imgData
18 | } from './config';
19 |
20 | import Loading from './loading';
21 | import Selector from './selector';
22 | import Editor from './editor';
23 |
24 | ((w, d) => {
25 | class Poster extends React.Component {
26 | constructor() {
27 | super();
28 |
29 | this.state = {
30 | num : 0,
31 | position : '',
32 | photo : '',
33 | sloganOne : '',
34 | sloganTwo : '',
35 | sloganThree: '',
36 | allowGen : false
37 | };
38 |
39 | this.onNumChange = this.onNumChange.bind(this);
40 | this.onPositionChange = this.onPositionChange.bind(this);
41 | this.handleSloganOneChange = this.handleSloganOneChange.bind(this);
42 | this.handleSloganTwoChange = this.handleSloganTwoChange.bind(this);
43 | this.handleSloganThreeChange = this.handleSloganThreeChange.bind(this);
44 | this.handlePhotoLoad = this.handlePhotoLoad.bind(this);
45 | this.donePosterPreview = this.donePosterPreview.bind(this);
46 | }
47 |
48 | componentDidMount() {
49 | setTimeout(() => {
50 | $('.poster').fullpage({
51 | touchSensitivity: 15,
52 | normalScrollElementTouchThreshold: 5
53 | });
54 |
55 | w.disableTouchMove = false;
56 | $.fn.fullpage.moveTo(2);
57 | }, 4000);
58 | }
59 |
60 |
61 | onNumChange(n) {
62 | this.setState({ num: n });
63 | }
64 |
65 | onPositionChange(pos) {
66 | this.setState({ position: pos });
67 | }
68 |
69 | handleSloganOneChange(content) {
70 | this.setState({ sloganOne: content });
71 | }
72 |
73 | handleSloganTwoChange(content) {
74 | this.setState({ sloganTwo: content });
75 | }
76 |
77 | handleSloganThreeChange(content) {
78 | this.setState({ sloganThree: content });
79 | }
80 |
81 | handlePhotoLoad(event) {
82 | let source = event.target.files[0],
83 | reader = new FileReader();
84 |
85 | reader.onload = (event) => {
86 | this.setState({ photo: event.target.result });
87 | };
88 |
89 | reader.readAsDataURL(source);
90 | }
91 |
92 | donePosterPreview() {
93 | this.setState({ allowGen: true });
94 | }
95 |
96 | render() {
97 | return (
98 |
99 |
100 |
116 | 0 ? imgData[this.state.num - 1].src : this.state.photo }
124 | themeColors={ themeColors }
125 | posterSize={ posterSize }
126 | titleOne={ photoTitle1 }
127 | titleTwo={ photoTitle2 }
128 | sloganArrow={ sloganArrow }
129 | footer={ footer }
130 | allowGen={ this.state.allowGen }
131 | donePosterPreview={ this.donePosterPreview }
132 | handleSloganOneChange={ this.handleSloganOneChange }
133 | handleSloganTwoChange={ this.handleSloganTwoChange }
134 | handleSloganThreeChange={ this.handleSloganThreeChange }
135 | />
136 |
137 | );
138 | }
139 | };
140 |
141 | try {
142 | ReactDOM.render(, $('#poster')[0]);
143 |
144 | w.disableTouchMove = true;
145 |
146 | $(w).on('touchmove', (event) => {
147 | if (disableTouchMove) {
148 | event.preventDefault();
149 | }
150 | });
151 | } catch(error) {
152 | $('.result').fullpage({
153 | touchSensitivity: 15,
154 | normalScrollElementTouchThreshold: 5
155 | });
156 |
157 | $('#download').click(() => {
158 | $('.download-info').addClass('active');
159 | });
160 |
161 | $('#new').click(() => {
162 | w.location = '/poster';
163 | });
164 |
165 | $('#share').click(() => {
166 | $('.share-info').addClass('active');
167 | });
168 |
169 | $('.close').click(() => {
170 | $('.download-info').removeClass('active');
171 | $('.share-info').removeClass('active');
172 | });
173 | }
174 | })(window, document);
--------------------------------------------------------------------------------
/src/scss/components/_selector.scss:
--------------------------------------------------------------------------------
1 | // _selector.scss
2 |
3 | @import '../../node_modules/swiper/dist/css/swiper.css';
4 |
5 | $prevWidth: 69vw;
6 | $prevHeight: 1.6 * $prevWidth;
7 |
8 | .selector {
9 | display: block;
10 | position: relative;
11 | width: 100%;
12 | height: 100vh;
13 | padding-top: 5.5vh;
14 |
15 | .hide {
16 | display: none !important;
17 | }
18 |
19 | .bg {
20 | background-color: $lgray;
21 | }
22 |
23 | .title {
24 | display: block;
25 | position: relative;
26 | width: 100%;
27 | text-align: center;
28 | font-size: 0.875rem;
29 | color: $dgray;
30 | margin-bottom: 3.75vh;
31 |
32 | .pos-show,
33 | .pos-edit {
34 | display: inline-block;
35 | position: relative;
36 | font: {
37 | size: 1.375rem;
38 | weight: 400;
39 | }
40 | color: $black;
41 | line-height: 1em;
42 | margin-left: 0.25em;
43 | }
44 |
45 | .pos-edit {
46 | padding: 0;
47 | border: none;
48 | }
49 |
50 | .pos-status {
51 | display: inline-block;
52 | position: absolute;
53 | top: 0.625rem;
54 | right: 6vw;
55 | height: 1em;
56 | font-size: 0.75rem;
57 | line-height: 1em;
58 |
59 | &.edit {
60 | svg {
61 | width: 0.75rem;
62 | height: 0.75rem;
63 | margin-right: 0.25em;
64 | }
65 | }
66 | }
67 | }
68 |
69 | .demos {
70 | display: block;
71 | position: relative;
72 | width: 100%;
73 | height: 100%;
74 | padding-top: 5px;
75 |
76 | .swiper-wrapper {
77 | position: relative;
78 | width: 100%;
79 |
80 | .swiper-slide {
81 | display: block;
82 | position: relative;
83 | width: $prevWidth;
84 | height: $prevHeight;
85 | padding: 4.666666667vw 3vw;
86 | box-shadow: 2px 2px 10px $dgray;
87 |
88 | &:nth-child(4n+1) {
89 | background-color: $sblue;
90 | }
91 |
92 | &:nth-child(4n+2) {
93 | background-color: $sgreen;
94 | }
95 |
96 | &:nth-child(4n+3) {
97 | background-color: $spink;
98 | }
99 |
100 | &:nth-child(4n) {
101 | background-color: $syellow;
102 | }
103 |
104 | .title-1 {
105 | display: block;
106 | position: relative;
107 | width: 42.666666667vw;
108 | margin-bottom: 1.6vw;
109 | }
110 |
111 | .title-2 {
112 | display: block;
113 | position: relative;
114 | width: 21.333333333vw;
115 | margin-bottom: 4vw;
116 | }
117 |
118 | > ul {
119 | display: block;
120 | position: relative;
121 | width: 100%;
122 | margin: 0;
123 | margin-bottom: 10vw;
124 | padding: 0;
125 | list-style: none;
126 |
127 | li {
128 | display: block;
129 | position: relative;
130 | width: 100%;
131 | font: {
132 | size: 2.133333333vw;
133 | weight: 300;
134 | }
135 | margin-bottom: 2.4vw;
136 | line-height: 1em;
137 |
138 | img {
139 | display: inline-block;
140 | position: relative;
141 | width: 1.333333333vw;
142 | margin-right: 0.25em;
143 | }
144 | }
145 | }
146 |
147 | .character {
148 | display: block;
149 | position: relative;
150 | width: 100%;
151 | }
152 |
153 | .character-diy {
154 | display: block;
155 | position: absolute;
156 | left: 0;
157 | width: 100%;
158 | height: 33vh;
159 | opacity: 0.15;
160 | z-index: 5;
161 | }
162 |
163 | .diy-photo {
164 | width: 0.1px;
165 | height: 0.1px;
166 | opacity: 0;
167 | overflow: hidden;
168 | position: absolute;
169 | z-index: -1;
170 | }
171 |
172 | .diy-photo + label {
173 | display: block;
174 | position: absolute;
175 | width: 18vw;
176 | height: 18vw;
177 | left: 25.5vw;
178 | margin-top: ($prevHeight - 18vw - 4.666666667vw*2)/4;
179 | border: 1px dashed $dgray;
180 | border-radius: 50%;
181 | cursor: pointer;
182 | padding: 5vw;
183 | z-index: 10;
184 |
185 | i:first-child {
186 | display: block;
187 | position: absolute;
188 | top: 9vw;
189 | width: 8vw;
190 | height: 1px;
191 | background-color: $dgray;
192 | }
193 |
194 | i:last-child {
195 | display: block;
196 | position: absolute;
197 | left: 9vw;
198 | width: 1px;
199 | height: 8vw;
200 | background-color: $dgray;
201 | }
202 | }
203 | }
204 | }
205 | }
206 |
207 | .next-edit {
208 | display: block;
209 | position: absolute;
210 | width: 100%;
211 | bottom: 0;
212 |
213 | span {
214 | display: block;
215 | position: relative;
216 | width: 100%;
217 | text-align: center;
218 | font-size: 3.2vw;
219 | line-height: 1em;
220 | margin-bottom: 4vw;
221 | color: $dgray;
222 | }
223 |
224 | .next-slide {
225 | display: block;
226 | position: relative;
227 | width: 4.67vw;
228 | height: 4.67vw;
229 | margin: 0 auto 3vh;
230 | animation: slideUp 1s infinite;
231 |
232 | svg {
233 | display: block;
234 | fill: $dgray;
235 | width: 4.67vw;
236 | height: 2.67vw;
237 | margin-bottom: -0.5vh;
238 | }
239 | }
240 | }
241 | }
--------------------------------------------------------------------------------
/src/scss/components/_result.scss:
--------------------------------------------------------------------------------
1 | // _result.scss
2 |
3 | .result {
4 | display: block;
5 | position: relative;
6 | width: 100%;
7 |
8 | .show {
9 | display: block;
10 | position: relative;
11 | width: 100vw;
12 | height: 100vh;
13 | padding: {
14 | top: 5vh;
15 | }
16 |
17 | .photo {
18 | display: block;
19 | position: relative;
20 | height: 72.5vh;
21 | margin: 0 auto 4.25vh;
22 | box-shadow: 2px 2px 10px $dgray;
23 | }
24 |
25 | .choose {
26 | display: block;
27 | position: relative;
28 | width: 80vw;
29 | margin: 0 auto;
30 | text-align: center;
31 |
32 | > a {
33 | display: inline-block;
34 | position: relative;
35 | border: 1px solid $border-gray {
36 | radius: 10px;
37 | }
38 | text-align: center;
39 | height: 8vw;
40 | width: 16vw;
41 | line-height: 8vw;
42 | color: $dgray;
43 | background-color: $white;
44 | font: {
45 | size: 3.6vw;
46 | weight: 400;
47 | }
48 | cursor: pointer;
49 | text-decoration: none;
50 |
51 | new {
52 | width: 39.2vw;
53 | background-color: $blue;
54 | color: $white;
55 | margin: 0 4.4vw;
56 | }
57 | }
58 | }
59 |
60 | .next {
61 | display: block;
62 | position: absolute;
63 | width: 100%;
64 | bottom: 0;
65 |
66 | span {
67 | display: block;
68 | position: relative;
69 | width: 100%;
70 | text-align: center;
71 | font-size: 3.2vw;
72 | line-height: 1em;
73 | margin-bottom: 2vw;
74 | color: $dgray;
75 | }
76 |
77 | .next-slide {
78 | display: block;
79 | position: relative;
80 | width: 4.67vw;
81 | height: 4.67vw;
82 | margin: 0 auto 2vh;
83 | animation: slideUp 1s infinite;
84 |
85 | svg {
86 | display: block;
87 | fill: $dgray;
88 | width: 4.67vw;
89 | height: 2.67vw;
90 | margin-bottom: -0.5vh;
91 | }
92 | }
93 | }
94 | }
95 |
96 | .more {
97 | display: block;
98 | position: relative;
99 | width: 100vw;
100 | height: 100vh;
101 | padding: 7vh 6.4vw;
102 |
103 | .next {
104 | display: block;
105 | position: absolute;
106 | width: 100%;
107 | bottom: 0;
108 | left: 0;
109 |
110 | span {
111 | display: block;
112 | position: relative;
113 | width: 100%;
114 | text-align: center;
115 | font-size: 3.2vw;
116 | line-height: 1em;
117 | margin-bottom: 2vw;
118 | color: $dgray;
119 | }
120 |
121 | .next-slide {
122 | display: block;
123 | position: relative;
124 | width: 4.67vw;
125 | height: 4.67vw;
126 | margin: 0 auto 2vh;
127 | animation: slideUp 1s infinite;
128 |
129 | svg {
130 | display: block;
131 | fill: $dgray;
132 | width: 4.67vw;
133 | height: 2.67vw;
134 | margin-bottom: -0.5vh;
135 | }
136 | }
137 | }
138 |
139 | .to {
140 | display: block;
141 | position: relative;
142 | width: 100%;
143 | line-height: 1em;
144 | font: {
145 | size: 4.4vw;
146 | weight: 400;
147 | }
148 | color: $dgray;
149 | margin-bottom: 5vh;
150 | }
151 |
152 | .sentences {
153 | display: block;
154 | position: relative;
155 | width: 100%;
156 | margin: 0;
157 | padding: 0;
158 | list-style: none;
159 |
160 | li {
161 | display: block;
162 | position: relative;
163 | width: 100%;
164 | line-height: 1em;
165 | font: {
166 | size: 4.4vw;
167 | weight: 400;
168 | }
169 | color: $black;
170 | margin-bottom: 2.5vh;
171 | text-align: center;
172 |
173 | &:last-child {
174 | margin-bottom: 5.5vh;
175 | }
176 | }
177 | }
178 |
179 | .from {
180 | display: block;
181 | position: relative;
182 | width: 100%;
183 | line-height: 1em;
184 | font: {
185 | size: 4.4vw;
186 | weight: 400;
187 | }
188 | color: $dgray;
189 | margin-bottom: 11.5vh;
190 | text-align: right;
191 | }
192 |
193 | .qr {
194 | display: block;
195 | position: relative;
196 | width: 52.4vw;
197 | margin: 0 auto;
198 | }
199 | }
200 | }
201 |
202 | .download-info,
203 | .share-info {
204 | display: none;
205 | position: absolute;
206 | width: 100vw;
207 | height: 100vh;
208 | top: 0;
209 | left: 0;
210 | background-color: rgba(0, 0, 0, 0.7);
211 | padding-top: 42.5vh;
212 | z-index: 10;
213 |
214 | .close {
215 | display: block;
216 | position: absolute;
217 | width: 5.6vw;
218 | height: 5.6vw;
219 | top: 13.5vh;
220 | right: 4vw;
221 | cursor: pointer;
222 | transform: rotate(45deg);
223 |
224 | &:before {
225 | content: '';
226 | display: block;
227 | position: absolute;
228 | top: 2.8vw;
229 | width: 5.6vw;
230 | height: 2px;
231 | background-color: $white;
232 | }
233 |
234 | &:after {
235 | content: '';
236 | display: block;
237 | position: absolute;
238 | left: 2.8vw;
239 | width: 2px;
240 | height: 5.6vw;
241 | background-color: $white;
242 | }
243 | }
244 |
245 | svg {
246 | display: block;
247 | position: relative;
248 | fill: $white;
249 | width: 10.4vw;
250 | height: 10.4vw;
251 | margin: 0 auto 3vh;
252 | }
253 |
254 | .info {
255 | display: block;
256 | position: relative;
257 | width: 100%;
258 | text-align: center;
259 | color: $white;
260 | font: {
261 | size: 3.6vw;
262 | weight: 400;
263 | }
264 | }
265 |
266 | &.active {
267 | display: block;
268 | }
269 | }
--------------------------------------------------------------------------------
/src/js/selector.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import $ from 'jquery';
3 | import Swiper from 'swiper';
4 |
5 | export default class extends React.Component {
6 | constructor() {
7 | super();
8 |
9 | this.handlePositionChange = this.handlePositionChange.bind(this);
10 | this.editPosition = this.editPosition.bind(this);
11 | this.quitEditPosition = this.quitEditPosition.bind(this);
12 | }
13 |
14 | componentDidMount() {
15 | this.props.handleNumChange(1);
16 | this.props.handlePositionChange(this.props.data[1].name);
17 |
18 | let that = this, swiper = new Swiper('.swiper-container', {
19 | effect: 'coverflow',
20 | centeredSlides: true,
21 | slidesPerView: 'auto',
22 | initialSlide : 1,
23 | longSwipes: false,
24 | followFinger: false,
25 | CSSWidthAndHeight: true,
26 | coverflow: {
27 | rotate: 50,
28 | stretch: 0,
29 | depth: 100,
30 | modifier: 1,
31 | slideShadows: false
32 | },
33 |
34 | onSlideNextStart: (swiper) => {
35 | let n = that.props.data[that.props.posNum].name,
36 | one = that.props.data[that.props.posNum].slogan1,
37 | two = that.props.data[that.props.posNum].slogan2,
38 | three = that.props.data[that.props.posNum].slogan3;
39 |
40 | that.props.handleNumChange(that.props.posNum + 1);
41 | that.props.handlePositionChange(n);
42 | that.props.handleSloganOneChange(one);
43 | that.props.handleSloganTwoChange(two);
44 | that.props.handleSloganThreeChange(three);
45 | },
46 | onSlidePrevStart: (swiper) => {
47 | let n = that.props.posNum > 1 ? that.props.data[that.props.posNum - 2].name : that.props.dName,
48 | one = that.props.posNum > 1 ? that.props.data[that.props.posNum - 2].slogan1 : '',
49 | two = that.props.posNum > 1 ? that.props.data[that.props.posNum - 2].slogan2 : '',
50 | three = that.props.posNum > 1 ? that.props.data[that.props.posNum - 2].slogan3 : '';
51 |
52 | that.props.handleNumChange(that.props.posNum - 1);
53 | that.props.handlePositionChange(n);
54 | that.props.handleSloganOneChange(one);
55 | that.props.handleSloganTwoChange(two);
56 | that.props.handleSloganThreeChange(three);
57 | }
58 | });
59 | }
60 |
61 | handlePositionChange(event) {
62 | this.props.handlePositionChange(event.target.value);
63 | }
64 |
65 | editPosition(event) {
66 | event.stopPropagation();
67 |
68 | $('#pos_show').addClass('hide');
69 | $('#pos_status_edit').addClass('hide');
70 | $('#pos_edit').removeClass('hide').focus();
71 | $('#pos_status_res').removeClass('hide');
72 | }
73 |
74 | quitEditPosition(event) {
75 | $('#pos_show').removeClass('hide');
76 | $('#pos_status_edit').removeClass('hide');
77 | $('#pos_edit').addClass('hide');
78 | $('#pos_status_res').addClass('hide');
79 | }
80 |
81 | render() {
82 | let that = this,
83 | demos = this.props.data.map((d, i) => {
84 | return (
85 |
89 |

90 |

91 |
92 | -
93 |
94 | { d.slogan1 }
95 |
96 | -
97 |
98 | { d.slogan2 }
99 |
100 | -
101 |
102 | { d.slogan3 }
103 |
104 |
105 |

106 |
107 | );
108 | });
109 |
110 | return (
111 |
115 |
116 |
我是
117 |
122 | { this.props.posName }
123 |
124 |
{event.stopPropagation();} }
132 | size={ this.props.posName.length + 1 }
133 | minLength='1'
134 | maxLength={ this.props.posLimit }
135 | />
136 |
141 |
142 | 编辑
143 |
144 |
148 | 剩余{ this.props.posLimit - this.props.posName.length }字
149 |
150 |
151 |
152 |
153 |
157 |

158 |

159 |
160 | -
161 |
162 | 本页可以编辑职位头衔
163 |
164 | -
165 |
166 | 套路口号在下一页修改
167 |
168 | -
169 |
170 | 先预览才能生成发布
171 |
172 |
173 |

177 |
184 |
188 |
189 | { demos }
190 |
191 |
192 |
193 |
编辑文案
194 |
195 |
196 |
197 |
198 |
199 |
200 | );
201 | }
202 | }
--------------------------------------------------------------------------------
/src/js/editor.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery';
2 | import React from 'react';
3 |
4 | export default class extends React.Component {
5 | constructor() {
6 | super();
7 |
8 | this.state = {
9 | sloganOne : '',
10 | sloganTwo : '',
11 | sloganThree: '',
12 | color : 'white'
13 | };
14 |
15 | this.handleSloganOneChange = this.handleSloganOneChange.bind(this);
16 | this.handleSloganTwoChange = this.handleSloganTwoChange.bind(this);
17 | this.handleSloganThreeChange = this.handleSloganThreeChange.bind(this);
18 | this.handleColorSelect = this.handleColorSelect.bind(this);
19 | this.genPoster = this.genPoster.bind(this);
20 | this.onPosterPreview = this.onPosterPreview.bind(this);
21 | this.onPosterGenerate = this.onPosterGenerate.bind(this);
22 | this.hideCanvas = this.hideCanvas.bind(this);
23 | }
24 |
25 | componentWillReceiveProps(nextProps) {
26 | this.setState({
27 | sloganOne : nextProps.sloganOne,
28 | sloganTwo : nextProps.sloganTwo,
29 | sloganThree: nextProps.sloganThree
30 | });
31 | }
32 |
33 | handleSloganOneChange(event) {
34 | this.props.handleSloganOneChange(event.target.value);
35 | }
36 |
37 | handleSloganTwoChange(event) {
38 | this.props.handleSloganTwoChange(event.target.value);
39 | }
40 |
41 | handleSloganThreeChange(event) {
42 | this.props.handleSloganThreeChange(event.target.value);
43 | }
44 |
45 | handleColorSelect(event) {
46 | $(event.target).addClass('active').siblings().map((i, arr) => { $(arr).removeClass('active'); });
47 | this.setState({ color: event.target.dataset.color });
48 | }
49 |
50 | genPoster() {
51 | let that = this,
52 | canvas = $('#poster_canvas')[0],
53 | context = canvas.getContext('2d'),
54 | title1 = new Image(),
55 | title2 = new Image(),
56 | arrow = new Image(),
57 | photo = new Image(),
58 | footer = new Image();
59 |
60 | title1.onload = () => {
61 | context.drawImage(title1, 29.8, 53.3);
62 | };
63 |
64 | title2.onload = () => {
65 | context.drawImage(title2, 29.8, 126.6);
66 | };
67 |
68 | arrow.onload = () => {
69 | context.drawImage(arrow, 29.8, 231.1);
70 | context.drawImage(arrow, 29.8, 290.2);
71 | context.drawImage(arrow, 29.8, 345.8);
72 | };
73 |
74 | photo.onload = () => {
75 | context.drawImage(photo, 0, 492, 750, 659);
76 | };
77 |
78 | footer.onload = () => {
79 | context.drawImage(footer, 0, that.props.posterSize.mainHe);
80 | };
81 |
82 | context.fillStyle = that.props.themeColors[that.state.color];
83 | context.fillRect(0, 0, that.props.posterSize.width, that.props.posterSize.mainHe);
84 |
85 | context.fillStyle = '#fff';
86 | context.fillRect(0, that.props.posterSize.mainHe, that.props.posterSize.width, that.props.posterSize.height);
87 |
88 | title1.src = that.props.titleOne;
89 | title2.src = that.props.titleTwo;
90 | arrow.src = that.props.sloganArrow;
91 |
92 | context.font = '28px Source Han Sans, Helvetica Neue, Microsoft Yahei';
93 | context.fillStyle = '#000';
94 |
95 | context.fillText(that.state.sloganOne, 57.2, 255);
96 | context.fillText(that.state.sloganTwo, 57.2, 310.8);
97 | context.fillText(that.state.sloganThree, 57.2, 366.3);
98 |
99 | context.font = '24px Source Han Sans, Helvetica Neue, Microsoft Yahei';
100 | context.fillStyle = '#727171';
101 |
102 | context.fillText('—— From ' + that.props.posName, 29.8, 437.7);
103 |
104 | photo.src = that.props.photo;
105 |
106 | footer.src = that.props.footer;
107 | }
108 |
109 | onPosterPreview(event) {
110 | event.stopPropagation();
111 |
112 | $('#poster_canvas').addClass('active');
113 | $('.poster-bg').addClass('active');
114 |
115 | this.genPoster();
116 | this.props.donePosterPreview();
117 | }
118 |
119 | onPosterGenerate(event) {
120 | event.stopPropagation();
121 |
122 | let img = $('#poster_canvas')[0].toDataURL(),
123 | data = {
124 | position: this.props.posName,
125 | slogan : this.state.sloganOne
126 | };
127 |
128 | $.ajax({
129 | type: 'POST',
130 | url : '/poster/upload',
131 | data: {
132 | pic : img,
133 | data: JSON.stringify(data)
134 | }
135 | }).done((result) => {
136 | window.location = '/poster/detail/' + (JSON.parse(result)).key;
137 | });
138 | }
139 |
140 | hideCanvas() {
141 | $('#poster_canvas').removeClass('active');
142 | $('.poster-bg').removeClass('active');
143 | }
144 |
145 | render() {
146 | return (
147 |
151 |
用毕生代码修炼的三行套路
152 |
每行可输入 { this.props.sloganLimit } 字
153 |
182 |
选择背景色
183 |
184 |
189 |
194 |
199 |
204 |
209 |
210 |
211 | -
216 | 生成
217 |
218 | -
222 | 预览
223 |
224 |
225 |
231 |
232 |
233 | );
234 | }
235 | }
--------------------------------------------------------------------------------
/dist/poster/icons.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------