├── public
├── favicon.ico
├── images
│ ├── grid.gif
│ ├── next.png
│ ├── prev.png
│ ├── close.png
│ ├── helix.gif
│ ├── loading.gif
│ ├── sphere.gif
│ ├── table.gif
│ └── qrcode_for_gh_15b711976a8a_258.jpg
├── index.php
├── .htaccess
├── wall2
│ ├── stats.min.js
│ ├── CSS3DRenderer.js
│ ├── tween.js
│ └── OrbitControls.js
├── css
│ ├── lightbox.min.css
│ ├── wall.css
│ └── wall2.css
└── js
│ ├── tween.min.js
│ ├── lazyload.js
│ ├── CSS3DRenderer.js
│ ├── lightbox.min.js
│ ├── wall.js
│ ├── TrackballControls.js
│ └── lightbox.js
├── screenshots
├── Cube.jpg
├── wall.jpg
├── Plane.jpg
├── Sphere.jpg
└── Cylinder.jpg
├── logs
└── README.md
├── src
├── middleware.php
├── controllers
│ ├── Admin
│ │ └── homeController.php
│ ├── Users
│ │ └── PhotosController.php
│ └── Weixin
│ │ └── IndexController.php
├── common
│ ├── function
│ │ └── file.php
│ ├── Curl
│ │ ├── Curlex.php
│ │ └── error.ini
│ ├── curl
│ │ ├── Curlex.php
│ │ └── error.ini
│ ├── Image
│ │ └── Photo.php
│ └── image
│ │ └── Photo.php
├── console
│ ├── Base.php
│ └── Qn.php
├── routes.php
├── models
│ ├── Qiniu.php
│ ├── User.php
│ └── Message.php
├── dependencies.php
└── settings.defailt.php
├── .gitignore
├── phpunit.xml
├── docker-compose.yml
├── cli.php
├── CONTRIBUTING.md
├── templates
├── index.phtml
├── subscribe.phtml
├── wall.phtml
└── wall2.phtml
├── tests
└── Functional
│ ├── HomepageTest.php
│ └── BaseTestCase.php
├── composer.json
├── README_CN.md
├── README.md
└── db
└── pper.sql
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/screenshots/Cube.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/screenshots/Cube.jpg
--------------------------------------------------------------------------------
/screenshots/wall.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/screenshots/wall.jpg
--------------------------------------------------------------------------------
/public/images/grid.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/grid.gif
--------------------------------------------------------------------------------
/public/images/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/next.png
--------------------------------------------------------------------------------
/public/images/prev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/prev.png
--------------------------------------------------------------------------------
/screenshots/Plane.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/screenshots/Plane.jpg
--------------------------------------------------------------------------------
/screenshots/Sphere.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/screenshots/Sphere.jpg
--------------------------------------------------------------------------------
/logs/README.md:
--------------------------------------------------------------------------------
1 | Your Slim Framework application's log files will be written to this directory.
2 |
--------------------------------------------------------------------------------
/public/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/close.png
--------------------------------------------------------------------------------
/public/images/helix.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/helix.gif
--------------------------------------------------------------------------------
/public/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/loading.gif
--------------------------------------------------------------------------------
/public/images/sphere.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/sphere.gif
--------------------------------------------------------------------------------
/public/images/table.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/table.gif
--------------------------------------------------------------------------------
/screenshots/Cylinder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/screenshots/Cylinder.jpg
--------------------------------------------------------------------------------
/src/middleware.php:
--------------------------------------------------------------------------------
1 | add(new \Slim\Csrf\Guard);
5 |
--------------------------------------------------------------------------------
/public/images/qrcode_for_gh_15b711976a8a_258.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuhill/pper/HEAD/public/images/qrcode_for_gh_15b711976a8a_258.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /composer.lock
2 | /vendor/
3 | /logs/*
4 | !/logs/README.md
5 | /public/photo/*
6 | /public/resource
7 | /.idea/
8 | /src/settings.php
9 | /db
10 | !/db/pper.sql
11 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tests
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/controllers/Admin/homeController.php:
--------------------------------------------------------------------------------
1 | '']
16 |
17 | $qn = new Qn($configure['settings']);
18 |
19 | $application = new Application();
20 |
21 | $application->add($qn); // 添加命令行
22 |
23 | $application->run();
--------------------------------------------------------------------------------
/src/common/function/file.php:
--------------------------------------------------------------------------------
1 | read())
15 | {
16 | if($file !== "." && $file !== "..")
17 | {
18 | self::get_allfiles($path."/".$file, $files);
19 | }
20 | }
21 | $dp ->close();
22 | }
23 | if(is_file($path))
24 | {
25 | $files[] = $path;
26 | }
27 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | ## Pull Requests
4 |
5 | 1. Fork the Slim Skeleton repository
6 | 2. Create a new branch for each feature or improvement
7 | 3. Send a pull request from each feature branch to the **3.x** branch
8 |
9 | It is very important to separate new features or improvements into separate feature branches, and to send a
10 | pull request for each branch. This allows us to review and pull in new features or improvements individually.
11 |
12 | ## Style Guide
13 |
14 | All pull requests must adhere to the [PSR-2 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md).
15 |
--------------------------------------------------------------------------------
/src/console/Base.php:
--------------------------------------------------------------------------------
1 | setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
27 | $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
28 | $this->db = $pdo;
29 |
30 | $this->qiniu = $settings['qiniu'];
31 | parent::__construct();
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | run();
33 |
--------------------------------------------------------------------------------
/src/routes.php:
--------------------------------------------------------------------------------
1 | get('/home','\Controllers\Admin\HomeController:home');
9 | $app->get('/users/[{id}]','\Controllers\Users\PhotosController:index');
10 | $app->get('/wall/[{id}]','\Controllers\Users\PhotosController:wall');
11 | $app->get('/subscribe','\Controllers\Users\PhotosController:subscribe');
12 | $app->get('/','\Controllers\Users\PhotosController:wall');
13 | $app->get('/weixin','\Controllers\Weixin\IndexController:vaild');
14 | $app->get('/ttt','\Controllers\Weixin\IndexController:ttt');
15 | $app->post('/weixin','\Controllers\Weixin\IndexController:index');
16 | /*
17 | $app->get('/[{name}]', function (Request $request, Response $response, array $args) {
18 | // Sample log message
19 | $this->logger->info("Slim-Skeleton '/' route");
20 |
21 | // Render index view
22 | return $this->renderer->render($response, 'index.phtml', $args);
23 | });
24 | */
25 |
26 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | RewriteEngine On
3 |
4 | # Some hosts may require you to use the `RewriteBase` directive.
5 | # Determine the RewriteBase automatically and set it as environment variable.
6 | # If you are using Apache aliases to do mass virtual hosting or installed the
7 | # project in a subdirectory, the base path will be prepended to allow proper
8 | # resolution of the index.php file and to redirect to the correct URI. It will
9 | # work in environments without path prefix as well, providing a safe, one-size
10 | # fits all solution. But as you do not need it in this case, you can comment
11 | # the following 2 lines to eliminate the overhead.
12 | RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$
13 | RewriteRule ^(.*) - [E=BASE:%1]
14 |
15 | # If the above doesn't work you might need to set the `RewriteBase` directive manually, it should be the
16 | # absolute physical path to the directory that contains this htaccess file.
17 | # RewriteBase /
18 |
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [QSA,L]
21 |
22 |
--------------------------------------------------------------------------------
/templates/index.phtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Slim 3
6 |
7 |
27 |
28 |
29 | Slim
30 | a microframework for PHP
31 |
32 |
33 | Hello = htmlspecialchars($name); ?>!
34 |
35 | Try SlimFramework
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/models/Qiniu.php:
--------------------------------------------------------------------------------
1 | accessKey = $config['access'];
23 | $this->secretKey = $config['secret'];
24 | $this->bucket = $config['bucket'];
25 | $this->url = $config['url'];
26 |
27 | }
28 |
29 | public function upload( $src ,$name = null){
30 | $auth = new Auth($this->accessKey, $this->secretKey);
31 | $bucketManager = new BucketManager($auth);
32 | $url = $src ;
33 | $key = is_null( $name )? "pper_" . md5($src) . ".jpg": $name ;
34 |
35 | // 指定抓取的文件保存名称
36 | list($ret, $err) = $bucketManager->fetch($url, $this->bucket, $key);
37 | //echo "=====> fetch $url to bucket: $this->bucket key: $key\n";
38 | if ($err !== null) {
39 | //var_dump($err);
40 | return false;
41 | } else {
42 | //print_r($ret);
43 | return $this->url . DIRECTORY_SEPARATOR . $ret['key'];
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/tests/Functional/HomepageTest.php:
--------------------------------------------------------------------------------
1 | runApp('GET', '/');
13 |
14 | $this->assertEquals(200, $response->getStatusCode());
15 | $this->assertContains('SlimFramework', (string)$response->getBody());
16 | $this->assertNotContains('Hello', (string)$response->getBody());
17 | }
18 |
19 | /**
20 | * Test that the index route with optional name argument returns a rendered greeting
21 | */
22 | public function testGetHomepageWithGreeting()
23 | {
24 | $response = $this->runApp('GET', '/name');
25 |
26 | $this->assertEquals(200, $response->getStatusCode());
27 | $this->assertContains('Hello name!', (string)$response->getBody());
28 | }
29 |
30 | /**
31 | * Test that the index route won't accept a post request
32 | */
33 | public function testPostHomepageNotAllowed()
34 | {
35 | $response = $this->runApp('POST', '/', ['test']);
36 |
37 | $this->assertEquals(405, $response->getStatusCode());
38 | $this->assertContains('Method not allowed', (string)$response->getBody());
39 | }
40 | }
--------------------------------------------------------------------------------
/src/dependencies.php:
--------------------------------------------------------------------------------
1 | getContainer();
5 |
6 | // view renderer
7 | $container['renderer'] = function ($c) {
8 | $settings = $c->get('settings')['renderer'];
9 | return new Slim\Views\PhpRenderer($settings['template_path']);
10 | };
11 |
12 | $container['view'] = new \Slim\Views\PhpRenderer('../templates/');
13 |
14 | // monolog
15 | $container['logger'] = function ($c) {
16 | $settings = $c->get('settings')['logger'];
17 | $logger = new Monolog\Logger($settings['name']);
18 | $logger->pushProcessor(new Monolog\Processor\UidProcessor());
19 | $logger->pushHandler(new Monolog\Handler\StreamHandler($settings['path'], $settings['level']));
20 | return $logger;
21 | };
22 |
23 |
24 | $c['errorHandler'] = function ($c) {
25 | return function ($request, $response, $exception) use ($c) {
26 | return $c['response']->withStatus(500)
27 | ->withHeader('Content-Type', 'text/html')
28 | ->write('Something went wrong!');
29 | };
30 | };
31 |
32 | // PDO database library
33 | $container['db'] = function ($c) {
34 | $settings = $c->get('settings')['db'];
35 | $pdo = new PDO("mysql:host=" . $settings['host'] . ";dbname=" . $settings['dbname'],
36 | $settings['user'], $settings['pass']);
37 | $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
38 | $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
39 | return $pdo;
40 | };
41 |
42 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slim/slim-skeleton",
3 | "description": "A Slim Framework skeleton application for rapid development",
4 | "keywords": ["microframework", "rest", "router", "psr7"],
5 | "homepage": "http://github.com/slimphp/Slim-Skeleton",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Josh Lockhart",
10 | "email": "info@joshlockhart.com",
11 | "homepage": "http://www.joshlockhart.com/"
12 | }
13 | ],
14 | "require": {
15 | "php": ">=5.5.0",
16 | "slim/slim": "^3.1",
17 | "slim/php-view": "^2.0",
18 | "monolog/monolog": "^1.17",
19 | "overtrue/wechat": "~4.1",
20 | "qiniu/php-sdk": "^7.2",
21 | "symfony/console": "^4.3"
22 | },
23 | "require-dev": {
24 | "phpunit/phpunit": ">=4.8 < 6.0"
25 | },
26 | "autoload-dev": {
27 | "psr-4": {
28 | "Tests\\": "tests/",
29 | "Controllers\\": "src/controllers/",
30 | "Common\\": "src/common/",
31 | "Models\\": "src/models/",
32 | "Console\\": "src/console/"
33 | }
34 | },
35 | "config": {
36 | "process-timeout" : 0
37 | },
38 | "scripts": {
39 | "start": "php -S localhost:8080 -t public",
40 | "test": "phpunit"
41 | },
42 | "repositories": {
43 | "packagist": {
44 | "type": "composer",
45 | "url": "https://packagist.phpcomposer.com"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # 拍拍客
2 | 基于微信互动的照片墙,使用slim3.0框架和easychat控件搭建
3 |
4 | # 效果
5 | Demo演示: http://www.pper.com.cn
6 |
7 | - 照片墙
8 |
9 | 
10 |
11 | - 水晶球
12 |
13 | 
14 |
15 | - 螺旋塔
16 |
17 | 
18 |
19 | - 展览厅
20 |
21 | 
22 |
23 |
24 |
25 | # 安装
26 | - 1 下载代码
27 | ```
28 | git clone https://github.com/liuhill/pper.git
29 | ```
30 |
31 | - 2 安装组件
32 | ```
33 | composer install
34 | ```
35 |
36 | - 3 生成自动加载
37 | ```
38 | composer dump-autoload -o
39 | ```
40 |
41 |
42 | - 4 创建mysql数据库`pper`,并导入数据表
43 |
44 | ```
45 | mysql -uroot -p pper < db/pper.sql
46 | ```
47 |
48 | - 5 复制配置文件`src/setting.default.php`为`src/setting.php`
49 | ```
50 | cp src/setting.default.php src/setting.php
51 | ```
52 |
53 | - 6 修改微信配置和数据库配置,文件 `src/setting.php`
54 |
55 | 微信
56 | ```
57 | // weixin
58 | 'weixin' => [
59 | 'app_id' => '', //app_id
60 | 'secret' => '', //EncodingAESKey
61 | 'token' => '', //token
62 |
63 | ],
64 | ```
65 | mysql
66 | ```
67 | // Database connection settings
68 | "db" => [
69 | "host" => "localhost",
70 | "dbname" => "pper",
71 | "user" => "",
72 | "pass" => ""
73 | ],
74 | ```
75 |
76 |
77 | - 4 启动服务
78 | 在根目录下执行
79 | ```
80 | php -S 0.0.0.0:80 -t public
81 | ```
--------------------------------------------------------------------------------
/templates/subscribe.phtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 关注拍拍客
7 |
31 |
32 |
33 | 拍拍客
34 |
35 |
36 |
操作方法
37 |
38 | - 1、关注公众号
39 | - 2、上传照片
40 | - 3、输入"我的"或者"mine",看自己的照片墙
41 |
42 |
43 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/settings.defailt.php:
--------------------------------------------------------------------------------
1 | [
4 | 'displayErrorDetails' => true, // set to false in production
5 | 'addContentLengthHeader' => false, // Allow the web server to send the content-length header
6 |
7 | // Renderer settings
8 | 'renderer' => [
9 | 'template_path' => __DIR__ . '/../templates/',
10 | ],
11 |
12 | // Monolog settings
13 | 'logger' => [
14 | 'name' => 'slim-app',
15 | 'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
16 | 'level' => \Monolog\Logger::DEBUG,
17 | ],
18 |
19 | // weixin
20 | 'weixin' => [
21 | 'app_id' => '', //测试号
22 | 'secret' => '',
23 | 'token' => '',
24 | 'log' => [
25 | 'level' => 'debug',
26 | 'file' => __DIR__ . '/../logs/weixin/easywechat.log',
27 | ]
28 | ],
29 |
30 | // 图片相关
31 | 'resource' => [
32 | 'path' => __DIR__ . "/../public/resource",
33 | 'url' => "http://".$_SERVER['HTTP_HOST'] . "/resource",
34 | ],
35 |
36 | // Database connection settings
37 | "db" => [
38 | "host" => "localhost",
39 | "dbname" => "pper",
40 | "user" => "",
41 | "pass" => ""
42 | ],
43 | // 七牛云
44 | 'qiniu' => [
45 | 'enable' => false,
46 | 'access' => '',
47 | 'secret' => '',
48 | 'bucket' => ''
49 | ],
50 |
51 | ],
52 | ];
53 |
--------------------------------------------------------------------------------
/src/common/Curl/Curlex.php:
--------------------------------------------------------------------------------
1 | 错误信息]
23 | */
24 | static public function get($url,$dest){
25 |
26 | $ch = curl_init();
27 | curl_setopt($ch, CURLOPT_URL, $url);
28 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
29 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
30 | $file = curl_exec($ch);
31 | $errorInfo = self::valid(curl_errno($ch));
32 | curl_close($ch);
33 |
34 | if($errorInfo === true){
35 | //$filename = pathinfo($url, PATHINFO_BASENAME);
36 | $resource = fopen($dest, 'a');
37 | fwrite($resource, $file);
38 | fclose($resource);
39 | return true;
40 | }
41 | else{
42 | return $errorInfo;
43 | }
44 |
45 | }
46 |
47 |
48 | /**
49 | * @param $errorNo
50 | * @return array|bool
51 | * curl的错误信息处理
52 | * 如果为0则返回true
53 | * 否则:返回错误信息[错误代码=>错误信息]
54 | *
55 | */
56 | static public function valid($errorNo){
57 | if(!$errorNo){
58 | return true;
59 | }
60 | $iniFile = __DIR__ . DIRECTORY_SEPARATOR . "error.ini";
61 | if(file_exists($iniFile)){
62 | $errorInfo = parse_ini_file($iniFile);
63 | $msg = $errorInfo[$errorNo];
64 | return "$errorNo:$msg";
65 | }
66 | else{
67 | return "$errorNo:未知错误信息编码";
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/common/curl/Curlex.php:
--------------------------------------------------------------------------------
1 | 错误信息]
23 | */
24 | static public function get($url,$dest){
25 |
26 | $ch = curl_init();
27 | curl_setopt($ch, CURLOPT_URL, $url);
28 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
29 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
30 | $file = curl_exec($ch);
31 | $errorInfo = self::valid(curl_errno($ch));
32 | curl_close($ch);
33 |
34 | if($errorInfo === true){
35 | //$filename = pathinfo($url, PATHINFO_BASENAME);
36 | $resource = fopen($dest, 'a');
37 | fwrite($resource, $file);
38 | fclose($resource);
39 | return true;
40 | }
41 | else{
42 | return $errorInfo;
43 | }
44 |
45 | }
46 |
47 |
48 | /**
49 | * @param $errorNo
50 | * @return array|bool
51 | * curl的错误信息处理
52 | * 如果为0则返回true
53 | * 否则:返回错误信息[错误代码=>错误信息]
54 | *
55 | */
56 | static public function valid($errorNo){
57 | if(!$errorNo){
58 | return true;
59 | }
60 | $iniFile = __DIR__ . DIRECTORY_SEPARATOR . "error.ini";
61 | if(file_exists($iniFile)){
62 | $errorInfo = parse_ini_file($iniFile);
63 | $msg = $errorInfo[$errorNo];
64 | return "$errorNo:$msg";
65 | }
66 | else{
67 | return "$errorNo:未知错误信息编码";
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/console/Qn.php:
--------------------------------------------------------------------------------
1 | setName('app:qn')
34 |
35 |
36 | // the short description shown while running "php bin/console list"
37 | ->setDescription('Refresh latest post on Algolia dataset.')
38 |
39 | // the full command description shown when running the command with
40 | // the "--help" option
41 | ->setHelp('This command allows you refresh Algolia dataset with latest post.')
42 |
43 | /** 参数
44 |
45 | ->addArgument('name', InputArgument::REQUIRED, 'Who do you want to greet?')
46 | ->addArgument('last_name', InputArgument::OPTIONAL, 'Your last name?')
47 | //*/
48 | ;
49 |
50 | }
51 |
52 | protected function execute(InputInterface $input, OutputInterface $output)
53 | {
54 |
55 | if( $this->qiniu['enable'] ) {
56 | $tbObj = new Message($this->db);
57 | $rows = $tbObj->getLocal();
58 | foreach($rows as $row) {
59 | $name = basename($row['resource']);
60 | $uid = $row['uid'];
61 | $qn = new Qiniu($this->qiniu);
62 | $url = $qn->upload($row['resource'],"pper_uid$uid"."_".$name);
63 | if( $url !== false ) {
64 | $tbObj->updateUrl($url,$row['id']);
65 | $output->writeLn($row['id']."上传成功");
66 | }
67 | }
68 |
69 | // Example code
70 | }
71 |
72 | else {
73 | $output->writeLn("上传七牛云设置为false");
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/public/wall2/stats.min.js:
--------------------------------------------------------------------------------
1 | // stats.js - http://github.com/mrdoob/stats.js
2 | var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";
3 | i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div");
4 | k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display=
5 | "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=
6 | a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};
--------------------------------------------------------------------------------
/src/common/Curl/error.ini:
--------------------------------------------------------------------------------
1 | ;
2 | ; HTTP curl error code info
3 | ; http://php.net/manual/en/curl.constants.php
4 | ;
5 |
6 | 1 = CURLE_UNSUPPORTED_PROTOCOL
7 | 2 = CURLE_FAILED_INIT
8 | 3 = CURLE_URL_MALFORMAT
9 | 4 = CURLE_URL_MALFORMAT_USER
10 | 5 = CURLE_COULDNT_RESOLVE_PROXY
11 | 6 = CURLE_COULDNT_RESOLVE_HOST
12 | 7 = CURLE_COULDNT_CONNECT
13 | 8 = CURLE_FTP_WEIRD_SERVER_REPLY
14 | 9 = CURLE_FTP_ACCESS_DENIED
15 | 10 = CURLE_FTP_USER_PASSWORD_INCORRECT
16 | 11 = CURLE_FTP_WEIRD_PASS_REPLY
17 | 12 = CURLE_FTP_WEIRD_USER_REPLY
18 | 13 = CURLE_FTP_WEIRD_PASV_REPLY
19 | 14 = CURLE_FTP_WEIRD_227_FORMAT
20 | 15 = CURLE_FTP_CANT_GET_HOST
21 | 16 = CURLE_FTP_CANT_RECONNECT
22 | 17 = CURLE_FTP_COULDNT_SET_BINARY
23 | 18 = CURLE_FTP_PARTIAL_FILE or CURLE_PARTIAL_FILE
24 | 19 = CURLE_FTP_COULDNT_RETR_FILE
25 | 20 = CURLE_FTP_WRITE_ERROR
26 | 21 = CURLE_FTP_QUOTE_ERROR
27 | 22 = CURLE_HTTP_NOT_FOUND or CURLE_HTTP_RETURNED_ERROR
28 | 23 = CURLE_WRITE_ERROR
29 | 24 = CURLE_MALFORMAT_USER
30 | 25 = CURLE_FTP_COULDNT_STOR_FILE
31 | 26 = CURLE_READ_ERROR
32 | 27 = CURLE_OUT_OF_MEMORY
33 | 28 = CURLE_OPERATION_TIMEDOUT or CURLE_OPERATION_TIMEOUTED
34 | 29 = CURLE_FTP_COULDNT_SET_ASCII
35 | 30 = CURLE_FTP_PORT_FAILED
36 | 31 = CURLE_FTP_COULDNT_USE_REST
37 | 32 = CURLE_FTP_COULDNT_GET_SIZE
38 | 33 = CURLE_HTTP_RANGE_ERROR
39 | 34 = CURLE_HTTP_POST_ERROR
40 | 35 = CURLE_SSL_CONNECT_ERROR
41 | 36 = CURLE_BAD_DOWNLOAD_RESUME or CURLE_FTP_BAD_DOWNLOAD_RESUME
42 | 37 = CURLE_FILE_COULDNT_READ_FILE
43 | 38 = CURLE_LDAP_CANNOT_BIND
44 | 39 = CURLE_LDAP_SEARCH_FAILED
45 | 40 = CURLE_LIBRARY_NOT_FOUND
46 | 41 = CURLE_FUNCTION_NOT_FOUND
47 | 42 = CURLE_ABORTED_BY_CALLBACK
48 | 43 = CURLE_BAD_FUNCTION_ARGUMENT
49 | 44 = CURLE_BAD_CALLING_ORDER
50 | 45 = CURLE_HTTP_PORT_FAILED
51 | 46 = CURLE_BAD_PASSWORD_ENTERED
52 | 47 = CURLE_TOO_MANY_REDIRECTS
53 | 48 = CURLE_UNKNOWN_TELNET_OPTION
54 | 49 = CURLE_TELNET_OPTION_SYNTAX
55 | 50 = CURLE_OBSOLETE
56 | 51 = CURLE_SSL_PEER_CERTIFICATE
57 | 52 = CURLE_GOT_NOTHING
58 | 53 = CURLE_SSL_ENGINE_NOTFOUND
59 | 54 = CURLE_SSL_ENGINE_SETFAILED
60 | 55 = CURLE_SEND_ERROR
61 | 56 = CURLE_RECV_ERROR
62 | 57 = CURLE_SHARE_IN_USE
63 | 58 = CURLE_SSL_CERTPROBLEM
64 | 59 = CURLE_SSL_CIPHER
65 | 60 = CURLE_SSL_CACERT
66 | 61 = CURLE_BAD_CONTENT_ENCODING
67 | 62 = CURLE_LDAP_INVALID_URL
68 | 63 = CURLE_FILESIZE_EXCEEDED
69 | 64 = CURLE_FTP_SSL_FAILED
70 | 79 = CURLE_SSH
--------------------------------------------------------------------------------
/src/common/curl/error.ini:
--------------------------------------------------------------------------------
1 | ;
2 | ; HTTP curl error code info
3 | ; http://php.net/manual/en/curl.constants.php
4 | ;
5 |
6 | 1 = CURLE_UNSUPPORTED_PROTOCOL
7 | 2 = CURLE_FAILED_INIT
8 | 3 = CURLE_URL_MALFORMAT
9 | 4 = CURLE_URL_MALFORMAT_USER
10 | 5 = CURLE_COULDNT_RESOLVE_PROXY
11 | 6 = CURLE_COULDNT_RESOLVE_HOST
12 | 7 = CURLE_COULDNT_CONNECT
13 | 8 = CURLE_FTP_WEIRD_SERVER_REPLY
14 | 9 = CURLE_FTP_ACCESS_DENIED
15 | 10 = CURLE_FTP_USER_PASSWORD_INCORRECT
16 | 11 = CURLE_FTP_WEIRD_PASS_REPLY
17 | 12 = CURLE_FTP_WEIRD_USER_REPLY
18 | 13 = CURLE_FTP_WEIRD_PASV_REPLY
19 | 14 = CURLE_FTP_WEIRD_227_FORMAT
20 | 15 = CURLE_FTP_CANT_GET_HOST
21 | 16 = CURLE_FTP_CANT_RECONNECT
22 | 17 = CURLE_FTP_COULDNT_SET_BINARY
23 | 18 = CURLE_FTP_PARTIAL_FILE or CURLE_PARTIAL_FILE
24 | 19 = CURLE_FTP_COULDNT_RETR_FILE
25 | 20 = CURLE_FTP_WRITE_ERROR
26 | 21 = CURLE_FTP_QUOTE_ERROR
27 | 22 = CURLE_HTTP_NOT_FOUND or CURLE_HTTP_RETURNED_ERROR
28 | 23 = CURLE_WRITE_ERROR
29 | 24 = CURLE_MALFORMAT_USER
30 | 25 = CURLE_FTP_COULDNT_STOR_FILE
31 | 26 = CURLE_READ_ERROR
32 | 27 = CURLE_OUT_OF_MEMORY
33 | 28 = CURLE_OPERATION_TIMEDOUT or CURLE_OPERATION_TIMEOUTED
34 | 29 = CURLE_FTP_COULDNT_SET_ASCII
35 | 30 = CURLE_FTP_PORT_FAILED
36 | 31 = CURLE_FTP_COULDNT_USE_REST
37 | 32 = CURLE_FTP_COULDNT_GET_SIZE
38 | 33 = CURLE_HTTP_RANGE_ERROR
39 | 34 = CURLE_HTTP_POST_ERROR
40 | 35 = CURLE_SSL_CONNECT_ERROR
41 | 36 = CURLE_BAD_DOWNLOAD_RESUME or CURLE_FTP_BAD_DOWNLOAD_RESUME
42 | 37 = CURLE_FILE_COULDNT_READ_FILE
43 | 38 = CURLE_LDAP_CANNOT_BIND
44 | 39 = CURLE_LDAP_SEARCH_FAILED
45 | 40 = CURLE_LIBRARY_NOT_FOUND
46 | 41 = CURLE_FUNCTION_NOT_FOUND
47 | 42 = CURLE_ABORTED_BY_CALLBACK
48 | 43 = CURLE_BAD_FUNCTION_ARGUMENT
49 | 44 = CURLE_BAD_CALLING_ORDER
50 | 45 = CURLE_HTTP_PORT_FAILED
51 | 46 = CURLE_BAD_PASSWORD_ENTERED
52 | 47 = CURLE_TOO_MANY_REDIRECTS
53 | 48 = CURLE_UNKNOWN_TELNET_OPTION
54 | 49 = CURLE_TELNET_OPTION_SYNTAX
55 | 50 = CURLE_OBSOLETE
56 | 51 = CURLE_SSL_PEER_CERTIFICATE
57 | 52 = CURLE_GOT_NOTHING
58 | 53 = CURLE_SSL_ENGINE_NOTFOUND
59 | 54 = CURLE_SSL_ENGINE_SETFAILED
60 | 55 = CURLE_SEND_ERROR
61 | 56 = CURLE_RECV_ERROR
62 | 57 = CURLE_SHARE_IN_USE
63 | 58 = CURLE_SSL_CERTPROBLEM
64 | 59 = CURLE_SSL_CIPHER
65 | 60 = CURLE_SSL_CACERT
66 | 61 = CURLE_BAD_CONTENT_ENCODING
67 | 62 = CURLE_LDAP_INVALID_URL
68 | 63 = CURLE_FILESIZE_EXCEEDED
69 | 64 = CURLE_FTP_SSL_FAILED
70 | 79 = CURLE_SSH
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pper
2 | The 3D photo gallery , Built using Slim 3, Three.js, Tween.js, easychat
3 |
4 | # Demo
5 | 
6 |
7 | http://www.pper.com.cn
8 |
9 |
10 | - Wall Show
11 |
12 | 
13 |
14 | - Cube Show
15 |
16 | 
17 |
18 | - Sphere Show
19 |
20 | 
21 |
22 | - Cylinder Show
23 |
24 | 
25 |
26 | - Plane
27 |
28 | 
29 |
30 |
31 |
32 | # Installation
33 | - 1、 Download source code
34 | ```
35 | git clone https://github.com/liuhill/pper.git
36 | ```
37 |
38 | - 2、 Install packages
39 | ```
40 | composer install
41 | ```
42 |
43 | - 3、 Generate autoload file
44 | ```
45 | composer dump-autoload -o
46 | ```
47 |
48 |
49 | - 4、 Create database `pper`, and import mysql table
50 |
51 | ```
52 | mysql -uroot -p pper < db/pper.sql
53 | ```
54 |
55 | - 5、 Copy Default Configure`src/setting.default.php`为`src/setting.php`
56 | ```
57 | cp src/setting.default.php src/setting.php
58 | ```
59 |
60 | - 6、 Configure `src/setting.php`
61 |
62 | Wechat
63 | ```
64 | // weixin
65 | 'weixin' => [
66 | 'app_id' => '', //app_id
67 | 'secret' => '', //EncodingAESKey
68 | 'token' => '', //token
69 |
70 | ],
71 | ```
72 |
73 | mysql
74 | ```
75 | // Database connection settings
76 | "db" => [
77 | "host" => "localhost",
78 | "dbname" => "pper",
79 | "user" => "",
80 | "pass" => ""
81 | ],
82 | ```
83 |
84 | qiniu cloud
85 |
86 | ```
87 | // 七牛云 https://www.qiniu.com/
88 | 'qiniu' => [
89 | 'enable' => false,
90 | 'access' => '',
91 | 'secret' => '',
92 | 'bucket' => ''
93 | ],
94 | ```
95 |
96 |
97 | - 7、 Start
98 |
99 | ```
100 | php -S 0.0.0.0:80 -t public
101 | ```
102 | OR
103 | ```
104 | composer start
105 | ```
--------------------------------------------------------------------------------
/src/models/User.php:
--------------------------------------------------------------------------------
1 | con = $db;
19 | }
20 |
21 |
22 | public function validUser($id){
23 | $sql = "SELECT * FROM `user` WHERE `id`=$id";
24 | $row = $this->con->query($sql)->fetchAll();
25 | if(count($row)>0){
26 | return $id;
27 | }
28 | return false;
29 | }
30 |
31 | public function getUid($user){
32 | $isMob='/^1[34578]{1}\d{9}$/';
33 | $isEmail = '/^([\.a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/';
34 | if(preg_match($isMob,$user)){ //手机号
35 | echo '手机号用户';
36 |
37 | }
38 | elseif(preg_match($isEmail,$user)) //邮箱
39 | {
40 | echo '邮箱用户';
41 | }
42 | else{ //openid或者nickname
43 | $sql = "SELECT * FROM `user` WHERE `nickname`='$user' OR `openid`='$user'";
44 | $row = $this->con->query($sql)->fetchAll();
45 | if(count($row)>0){
46 | return intval($row[0]['id']);
47 | }
48 | return false;
49 | }
50 |
51 | return false;
52 | }
53 |
54 | public function getUidByOpenid($openId){
55 | $uid = $this->getUid($openId);
56 | if($uid === false){
57 | $uid = $this->addUser("weixin",$openId);
58 | }
59 | return $uid;
60 | }
61 |
62 | public function addUser($type,$info){
63 |
64 | $ll = $ct = date("Y-m-d H:i:s");
65 | $db=$this->con;
66 | switch($type){
67 | case 'weixin':
68 | $sql = "INSERT INTO `user`(`openid`,`create_time`,`last_login`) VALUES(:openid,:createtime,:lasttime)";
69 | $stmt = $db->prepare($sql);
70 | $stmt->bindParam(":openid",$info);
71 | $stmt->bindParam(":createtime",$ct);
72 | $stmt->bindParam(":lasttime",$ll);
73 | $stmt->execute();
74 | $id = $db->lastInsertId();
75 | break;
76 | case 'mobile':
77 | break;
78 | case 'email':
79 | break;
80 | default:
81 | break;
82 | }
83 | return $id;
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/src/models/Message.php:
--------------------------------------------------------------------------------
1 | con = $db;
20 | }
21 |
22 | public function getLastMessage(){
23 |
24 | }
25 |
26 |
27 | public function getLocal(){
28 | $sql = "SELECT * FROM message WHERE resource LIKE \"http://www.pper.com.cn%\"";
29 | return $this->con->query($sql)->fetchAll();
30 | }
31 |
32 |
33 | public function getImages($uid) {
34 | $rows = $this->getMessage($uid);
35 | if(count($rows) < Message::COUNT){
36 | $fillRows = $this->getMessage(1,Message::COUNT - count($rows));
37 | $rows = array_merge($rows,$fillRows);
38 | }
39 |
40 | return $rows;
41 | }
42 |
43 | public function getMessage($uid,$count = Message::COUNT){
44 |
45 | $order = " ORDER BY create_time DESC limit " . $count;
46 | if(is_null($uid)) {
47 | $sql = "SELECT * FROM message " . $order;
48 | }
49 | else {
50 | $sql = "SELECT * FROM message WHERE uid=$uid " . $order;
51 | }
52 |
53 | //$db->prepare($sql);
54 | //$db->bindParam(":uid",$uid);
55 |
56 | return $this->con->query($sql)->fetchAll();
57 | }
58 |
59 | public function updateUrl($url,$id) {
60 | $sql = "UPDATE message SET resource=:resource WHERE id=:id";
61 | $stmt = $this->con->prepare($sql);
62 | $stmt->bindParam(":resource", $url);
63 | $stmt->bindParam(":id", $id);
64 | $res = $stmt->execute();
65 | return $res;
66 | }
67 |
68 | public function saveMessage($uid,$type,$content,$resource){
69 | $curData = date("Y-m-d H:i:s");
70 | $db = $this->con;
71 | $sql = "INSERT INTO `message`(`uid`,`type`,`content`,`resource`,`create_time`) VALUES (:uid,:resouceType,:content,:resource,:createtime)";
72 | $stmt = $db->prepare($sql);
73 | $stmt->bindParam(":uid", $uid);
74 | $stmt->bindParam(":resouceType", $type);
75 | $stmt->bindParam(":content", $content);
76 | $stmt->bindParam(":resource", $resource);
77 | $stmt->bindParam(":createtime", $curData);
78 | $res = $stmt->execute();
79 | return $res;
80 | }
81 | }
--------------------------------------------------------------------------------
/tests/Functional/BaseTestCase.php:
--------------------------------------------------------------------------------
1 | $requestMethod,
39 | 'REQUEST_URI' => $requestUri
40 | ]
41 | );
42 |
43 | // Set up a request object based on the environment
44 | $request = Request::createFromEnvironment($environment);
45 |
46 | // Add request data, if it exists
47 | if (isset($requestData)) {
48 | $request = $request->withParsedBody($requestData);
49 | }
50 |
51 | // Set up a response object
52 | $response = new Response();
53 |
54 | // Use the application settings
55 | $settings = require __DIR__ . '/../../src/settings.php';
56 |
57 | // Instantiate the application
58 | $app = new App($settings);
59 |
60 | // Set up dependencies
61 | require __DIR__ . '/../../src/dependencies.php';
62 |
63 | // Register middleware
64 | if ($this->withMiddleware) {
65 | require __DIR__ . '/../../src/middleware.php';
66 | }
67 |
68 | // Register routes
69 | require __DIR__ . '/../../src/routes.php';
70 |
71 | // Process the application
72 | $response = $app->process($request, $response);
73 |
74 | // Return the response
75 | return $response;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/public/css/lightbox.min.css:
--------------------------------------------------------------------------------
1 | .lb-loader,.lightbox{text-align:center;line-height:0}.lb-dataContainer:after,.lb-outerContainer:after{content:"";clear:both}html.lb-disable-scrolling{overflow:hidden;position:fixed;height:100vh;width:100vw}.lightboxOverlay{position:absolute;top:0;left:0;z-index:9999;background-color:#000;filter:alpha(Opacity=80);opacity:.8;display:none}.lightbox{position:absolute;left:0;width:100%;z-index:10000;font-weight:400}.lightbox .lb-image{display:block;height:auto;max-width:inherit;max-height:none;border-radius:3px;border:4px solid #fff}.lightbox a img{border:none}.lb-outerContainer{position:relative;width:250px;height:250px;margin:0 auto;border-radius:4px;background-color:#fff}.lb-loader,.lb-nav{position:absolute;left:0}.lb-outerContainer:after{display:table}.lb-loader{top:43%;height:25%;width:100%}.lb-cancel{display:block;width:32px;height:32px;margin:0 auto;background:url(../images/loading.gif) no-repeat}.lb-nav{top:0;height:100%;width:100%;z-index:10}.lb-container>.nav{left:0}.lb-nav a{outline:0;background-image:url(data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}.lb-next,.lb-prev{height:100%;cursor:pointer;display:block}.lb-nav a.lb-prev{width:34%;left:0;float:left;background:url(../images/prev.png) left 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-prev:hover{filter:alpha(Opacity=100);opacity:1}.lb-nav a.lb-next{width:64%;right:0;float:right;background:url(../images/next.png) right 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-next:hover{filter:alpha(Opacity=100);opacity:1}.lb-dataContainer{margin:0 auto;padding-top:5px;width:100%;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.lb-dataContainer:after{display:table}.lb-data{padding:0 4px;color:#ccc}.lb-data .lb-details{width:85%;float:left;text-align:left;line-height:1.1em}.lb-data .lb-caption{font-size:13px;font-weight:700;line-height:1em}.lb-data .lb-caption a{color:#4ae}.lb-data .lb-number{display:block;clear:left;padding-bottom:1em;font-size:12px;color:#999}.lb-data .lb-close{display:block;float:right;width:30px;height:30px;background:url(../images/close.png) top right no-repeat;text-align:right;outline:0;filter:alpha(Opacity=70);opacity:.7;-webkit-transition:opacity .2s;-moz-transition:opacity .2s;-o-transition:opacity .2s;transition:opacity .2s}.lb-data .lb-close:hover{cursor:pointer;filter:alpha(Opacity=100);opacity:1}
--------------------------------------------------------------------------------
/templates/wall.phtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 拍拍客
5 |
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 | -
20 | 我的照片墙
21 |
22 | -
23 | 首页
24 |
25 |
26 |
27 |
28 |
29 | 我们喜欢随手拍一拍,偶尔会看一看。我们是拍拍客!
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |

43 |
44 |
使用方法:
45 |
46 | - 1、微信添加公共账号
47 | - 2、发送图片
48 | - 3、刷新
49 |
50 |
51 |
52 |
58 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
73 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/db/pper.sql:
--------------------------------------------------------------------------------
1 | -- MySQL dump 10.16 Distrib 10.3.10-MariaDB, for osx10.14 (x86_64)
2 | --
3 | -- Host: localhost Database: pper
4 | -- ------------------------------------------------------
5 | -- Server version 10.3.10-MariaDB
6 |
7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10 | /*!40101 SET NAMES utf8 */;
11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12 | /*!40103 SET TIME_ZONE='+00:00' */;
13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17 |
18 | --
19 | -- Table structure for table `message`
20 | --
21 |
22 | DROP TABLE IF EXISTS `message`;
23 | /*!40101 SET @saved_cs_client = @@character_set_client */;
24 | /*!40101 SET character_set_client = utf8 */;
25 | CREATE TABLE `message` (
26 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
27 | `uid` int(11) NOT NULL COMMENT '用户id',
28 | `type` varchar(16) NOT NULL COMMENT '类型',
29 | `content` varchar(256) DEFAULT NULL COMMENT '文字内容',
30 | `resource` varchar(256) DEFAULT NULL COMMENT '资源路径',
31 | `create_time` datetime NOT NULL COMMENT '创建时间',
32 | PRIMARY KEY (`id`),
33 | KEY `uid` (`uid`),
34 | KEY `createdtime` (`create_time`)
35 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
36 | /*!40101 SET character_set_client = @saved_cs_client */;
37 |
38 | --
39 | -- Table structure for table `user`
40 | --
41 |
42 | DROP TABLE IF EXISTS `user`;
43 | /*!40101 SET @saved_cs_client = @@character_set_client */;
44 | /*!40101 SET character_set_client = utf8 */;
45 | CREATE TABLE `user` (
46 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
47 | `nickname` varchar(32) DEFAULT NULL COMMENT '昵称',
48 | `password` varchar(128) DEFAULT NULL COMMENT '密码',
49 | `mobile` varchar(32) DEFAULT NULL COMMENT '手机号',
50 | `email` varchar(64) DEFAULT NULL COMMENT '邮箱地址',
51 | `openid` varchar(128) NOT NULL COMMENT '微信在公众账号的openid',
52 | `create_time` datetime NOT NULL COMMENT '注册时间',
53 | `last_login` datetime NOT NULL COMMENT '上次登陆时间',
54 | PRIMARY KEY (`id`),
55 | UNIQUE KEY `nickname_UNIQUE` (`nickname`)
56 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
57 | /*!40101 SET character_set_client = @saved_cs_client */;
58 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
59 |
60 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
61 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
62 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
63 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
64 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
65 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
66 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
67 |
68 | -- Dump completed on 2019-01-07 13:56:22
69 |
--------------------------------------------------------------------------------
/src/controllers/Users/PhotosController.php:
--------------------------------------------------------------------------------
1 | container = $container;
24 | $this->view = $container->get('view');
25 | $this->resource = $container->get("settings")['resource'];
26 | $this->db = $container->get("db");
27 | }
28 |
29 | private function getImagesByUid($uid){
30 | $imgDir = $this->photoDir['resize'] . DIRECTORY_SEPARATOR . $uid;
31 | $imgUrl = $this->photoDir['resizeUrl'] . DIRECTORY_SEPARATOR . $uid;
32 | $originalUrl = $this->photoDir['originalUrl'] . DIRECTORY_SEPARATOR . $uid;
33 | $images = Photo::list($imgDir);
34 | foreach($images as $i=>$v){
35 | $images[$i]['url'] = $imgUrl .DIRECTORY_SEPARATOR .$v['name'];
36 | $images[$i]['original'] = $originalUrl .DIRECTORY_SEPARATOR .$v['name'];
37 | }
38 | //echo $images;
39 | return $images;
40 | }
41 |
42 | public function index($request, $response, $args){
43 | // $images = $this->getImagesByUid($uid);
44 |
45 |
46 | if(isset($args['id']) && !empty($args['id'])){
47 | $uid = intval($args['id']);
48 | }
49 | $mMessage = new Message($this->db);
50 | if (empty($uid)) {
51 | $resouce = $mMessage->getImages(1);
52 | } else {
53 | $resouce = $mMessage->getImages($uid);
54 | }
55 | $data = [];
56 | foreach($resouce as $i=>$v){
57 | $info = [];
58 | $info['unique_id'] = $v['id'];
59 | $info['type'] = $type = $v['type'];
60 | switch($type){
61 | case 'text':
62 | $info['text'] = $v['content'];
63 | break;
64 | case 'image':
65 | case 'voice':
66 | case 'video':
67 | case 'file':
68 | $info['src'] = $v['content'];
69 | $info['resource'] = $v['resource'];
70 | break;
71 | default:
72 | continue;
73 | }
74 | $data[] = $info;
75 | }
76 |
77 | return $response->withJson($data);
78 | }
79 |
80 | public function wall($request, $response, $args){
81 |
82 | if(isset($args['id']) && !empty($args['id'])){
83 | $uid = $args['id'];
84 | $mUser = new User($this->db);
85 | $uid = $mUser->validUser($uid);
86 | if($uid === false) {
87 | $uid = '';
88 | }
89 | }
90 | else {
91 | $uid = '';
92 | }
93 |
94 | $response = $this->view->render($response,
95 | 'wall2.phtml',
96 | [
97 | 'uid' => $uid
98 | ]);
99 | return $response;
100 |
101 | }
102 |
103 | public function subscribe($request, $response, $args){
104 | $response = $this->view->render($response,
105 | 'subscribe.phtml');
106 | return $response;
107 | }
108 | }
--------------------------------------------------------------------------------
/public/css/wall.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | }
4 |
5 | body {
6 | background-color: #000000;
7 | margin: 0;
8 | font-family: Helvetica, sans-serif;;
9 | overflow: hidden;
10 | }
11 |
12 | #info {
13 | position: absolute;
14 | width: 100%;
15 | padding: 5px;
16 | font-family: Monospace;
17 | font-size: 1em;
18 | font-weight: bold;
19 | text-align: center;
20 | z-index: 1;
21 | }
22 |
23 | #info a {
24 | text-decoration:none;
25 | color: #d4d2d2;
26 | }
27 |
28 | #info span {
29 | font-size: 1em;
30 | color: deepskyblue;
31 | }
32 |
33 | #info ul li {
34 | display:inline;
35 | }
36 |
37 | #info li {
38 | border-right: 1px solid white;
39 | margin-left: 0.4em;
40 | }
41 |
42 | #info li:first-child a {
43 | color: white;
44 | }
45 |
46 | #info li:last-child {
47 | border-right: 0px;
48 | }
49 |
50 |
51 | #info .slogon{
52 | color: #9a9898;
53 | font-size: .8em;
54 | }
55 |
56 |
57 | #weixin {
58 | font-size: 1em;
59 | color: white;
60 | margin-bottom: 1em;
61 | }
62 |
63 |
64 | #weixin ul{
65 | list-style-type:none;
66 | margin:0px;
67 | padding:0px;
68 | font-size: .8em;
69 | }
70 |
71 | #weixin img {
72 | width: 130px;
73 | height: 130px;
74 | }
75 |
76 | #demo {
77 | /*text-align: center;*/
78 | color: white;
79 | margin-bottom: 1em;
80 | }
81 |
82 | #demo img {
83 | width: 110px;
84 | height: 80px;
85 | opacity: 0.6;
86 | filter:alpha(opacity=60); /* 针对 IE8 以及更早的版本 */
87 | }
88 |
89 |
90 | #menu {
91 | position: absolute;
92 | bottom: 20px;
93 | width: 100%;
94 | text-align: center;
95 | font-size: 1em;
96 | }
97 |
98 | #code {
99 | z-index: 1;
100 | position: absolute;
101 | font-size: 0.5em;
102 | }
103 | #code a {
104 | color: red;
105 | }
106 |
107 | .element {
108 | width: 161px;
109 | height: 100px;
110 | box-shadow: 0px 0px 12px rgba(0,255,255,0.5);
111 | text-align: center;
112 | border: 1px solid rgba(127,255,255,0.25);
113 | cursor: default;
114 | }
115 |
116 | .element:hover {
117 | box-shadow: 0px 0px 12px rgba(0,255,255,0.75);
118 | border: 1px solid rgba(127,255,255,0.75);
119 | }
120 |
121 |
122 | .element img {
123 |
124 | margin: 10px;
125 | object-fit: none; /* Do not scale the image */
126 | object-position: center; /* Center the image within the element */
127 | width: 141px;
128 | height: 80px;
129 | }
130 |
131 | .element .number {
132 | position: absolute;
133 | top: 20px;
134 | right: 20px;
135 | font-size: 12px;
136 | color: rgba(127,255,255,0.75);
137 | }
138 |
139 | .element .symbol {
140 | position: absolute;
141 | top: 40px;
142 | left: 0px;
143 | right: 0px;
144 | font-size: 60px;
145 | font-weight: bold;
146 | color: rgba(255,255,255,0.75);
147 | text-shadow: 0 0 10px rgba(0,255,255,0.95);
148 | }
149 |
150 | .element .details {
151 | position: absolute;
152 | bottom: 15px;
153 | left: 0px;
154 | right: 0px;
155 | font-size: 12px;
156 | color: rgba(127,255,255,0.75);
157 | }
158 |
159 |
160 | button {
161 | color: rgba(127,255,255,0.75);
162 | background: transparent;
163 | outline: 1px solid rgba(127,255,255,0.75);
164 | border: 0px;
165 | padding: 5px 10px;
166 | cursor: pointer;
167 | }
168 | button:hover {
169 | background-color: rgba(0,255,255,0.5);
170 | }
171 | button:active {
172 | color: #000000;
173 | background-color: rgba(0,255,255,0.75);
174 | }
175 |
176 |
177 | @media screen and (max-width: 800px) { /*当屏幕尺寸小于600px时,应用下面的CSS样式*/
178 | #weixin {
179 | display: none;
180 | }
181 |
182 | #demo {
183 | display: none;
184 | }
185 |
186 | #code {
187 | display: none;
188 | }
189 | }
190 |
191 |
--------------------------------------------------------------------------------
/public/css/wall2.css:
--------------------------------------------------------------------------------
1 | body {
2 | /* set margin to 0 and overflow to hidden, to go fullscreen */
3 | margin: 0;
4 | overflow: hidden;
5 | background-color: black;
6 | }
7 |
8 | #someidentifier {
9 | position: fixed;
10 | z-index: 100;
11 | bottom: 0;
12 | left: 0;
13 | width: 100%;
14 | background-color: black;
15 | opacity: 0.7;
16 | color: #ffffff;
17 | font-family: Calibri;
18 | }
19 |
20 | #query {
21 | margin-left: 5px;
22 | }
23 |
24 | #info {
25 | position: absolute;
26 | width: 100%;
27 | padding: 5px;
28 | font-family: Monospace;
29 | font-size: 1em;
30 | font-weight: bold;
31 | text-align: center;
32 | z-index: 1;
33 | }
34 |
35 | #info a {
36 | text-decoration:none;
37 | color: #d4d2d2;
38 | }
39 |
40 | #info span {
41 | font-size: 1em;
42 | color: deepskyblue;
43 | }
44 |
45 | #info ul li {
46 | display:inline;
47 | }
48 |
49 | #info li {
50 | border-right: 1px solid white;
51 | margin-left: 0.4em;
52 | }
53 |
54 | #info li:first-child a {
55 | color: white;
56 | }
57 |
58 | #info li:last-child {
59 | border-right: 0px;
60 | }
61 |
62 |
63 | #info .slogon{
64 | color: #9a9898;
65 | font-size: .8em;
66 | }
67 |
68 |
69 | #weixin {
70 | font-size: 1em;
71 | color: white;
72 | position: fixed;
73 | right:0rem;
74 | bottom: 1rem;
75 | margin: 1rem;
76 | }
77 |
78 |
79 | #weixin ul{
80 | list-style-type:none;
81 | margin:0px;
82 | padding:0px;
83 | font-size: .8em;
84 | }
85 |
86 | #weixin img {
87 | width: 130px;
88 | height: 130px;
89 | }
90 |
91 | #demo {
92 | /*text-align: center;*/
93 | color: white;
94 | margin-bottom: 1em;
95 | }
96 |
97 | #demo img {
98 | width: 110px;
99 | height: 80px;
100 | opacity: 0.6;
101 | filter:alpha(opacity=60); /* 针对 IE8 以及更早的版本 */
102 | }
103 |
104 |
105 | #menu {
106 | position: absolute;
107 | bottom: 20px;
108 | width: 100%;
109 | text-align: center;
110 | font-size: 1em;
111 | }
112 |
113 | #code {
114 | z-index: 1;
115 | position: absolute;
116 | font-size: 0.5em;
117 | }
118 | #code a {
119 | color: red;
120 | }
121 |
122 | .element {
123 | width: 161px;
124 | height: 100px;
125 | box-shadow: 0px 0px 12px rgba(0,255,255,0.5);
126 | text-align: center;
127 | border: 1px solid rgba(127,255,255,0.25);
128 | cursor: default;
129 | }
130 |
131 | .element:hover {
132 | box-shadow: 0px 0px 12px rgba(0,255,255,0.75);
133 | border: 1px solid rgba(127,255,255,0.75);
134 | }
135 |
136 |
137 | .element img {
138 |
139 | margin: 10px;
140 | object-fit: none; /* Do not scale the image */
141 | object-position: center; /* Center the image within the element */
142 | width: 141px;
143 | height: 80px;
144 | }
145 |
146 | .element .number {
147 | position: absolute;
148 | top: 20px;
149 | right: 20px;
150 | font-size: 12px;
151 | color: rgba(127,255,255,0.75);
152 | }
153 |
154 | .element .symbol {
155 | position: absolute;
156 | top: 40px;
157 | left: 0px;
158 | right: 0px;
159 | font-size: 60px;
160 | font-weight: bold;
161 | color: rgba(255,255,255,0.75);
162 | text-shadow: 0 0 10px rgba(0,255,255,0.95);
163 | }
164 |
165 | .element .details {
166 | position: absolute;
167 | bottom: 15px;
168 | left: 0px;
169 | right: 0px;
170 | font-size: 12px;
171 | color: rgba(127,255,255,0.75);
172 | }
173 |
174 |
175 | button {
176 | color: rgba(127,255,255,0.75);
177 | background: transparent;
178 | outline: 1px solid rgba(127,255,255,0.75);
179 | border: 0px;
180 | padding: 5px 10px;
181 | cursor: pointer;
182 | }
183 | button:hover {
184 | background-color: rgba(0,255,255,0.5);
185 | }
186 | button:active {
187 | color: #000000;
188 | background-color: rgba(0,255,255,0.75);
189 | }
190 |
191 |
192 | @media screen and (max-width: 800px) { /*当屏幕尺寸小于600px时,应用下面的CSS样式*/
193 | #weixin {
194 | display: none;
195 | }
196 |
197 | #demo {
198 | display: none;
199 | }
200 |
201 | #code {
202 | display: none;
203 | }
204 | }
205 |
206 |
--------------------------------------------------------------------------------
/public/js/tween.min.js:
--------------------------------------------------------------------------------
1 | // tween.js - http://github.com/sole/tween.js
2 | 'use strict';var TWEEN=TWEEN||function(){var a=[];return{REVISION:"7",getAll:function(){return a},removeAll:function(){a=[]},add:function(c){a.push(c)},remove:function(c){c=a.indexOf(c);-1!==c&&a.splice(c,1)},update:function(c){if(0===a.length)return!1;for(var b=0,d=a.length,c=void 0!==c?c:Date.now();b(a*=2)?0.5*a*a:-0.5*(--a*(a-2)-1)}},Cubic:{In:function(a){return a*a*a},Out:function(a){return--a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a:0.5*((a-=2)*a*a+2)}},Quartic:{In:function(a){return a*a*a*a},Out:function(a){return 1- --a*a*a*a},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)}},Quintic:{In:function(a){return a*a*a*
7 | a*a},Out:function(a){return--a*a*a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)}},Sinusoidal:{In:function(a){return 1-Math.cos(a*Math.PI/2)},Out:function(a){return Math.sin(a*Math.PI/2)},InOut:function(a){return 0.5*(1-Math.cos(Math.PI*a))}},Exponential:{In:function(a){return 0===a?0:Math.pow(1024,a-1)},Out:function(a){return 1===a?1:1-Math.pow(2,-10*a)},InOut:function(a){return 0===a?0:1===a?1:1>(a*=2)?0.5*Math.pow(1024,a-1):0.5*(-Math.pow(2,-10*(a-1))+2)}},Circular:{In:function(a){return 1-
8 | Math.sqrt(1-a*a)},Out:function(a){return Math.sqrt(1- --a*a)},InOut:function(a){return 1>(a*=2)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)}},Elastic:{In:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return-(b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4))},Out:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return b*Math.pow(2,-10*a)*Math.sin((a-c)*
9 | 2*Math.PI/0.4)+1},InOut:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return 1>(a*=2)?-0.5*b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4):0.5*b*Math.pow(2,-10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4)+1}},Back:{In:function(a){return a*a*(2.70158*a-1.70158)},Out:function(a){return--a*a*(2.70158*a+1.70158)+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*(3.5949095*a-2.5949095):0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)}},Bounce:{In:function(a){return 1-
10 | TWEEN.Easing.Bounce.Out(1-a)},Out:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},InOut:function(a){return 0.5>a?0.5*TWEEN.Easing.Bounce.In(2*a):0.5*TWEEN.Easing.Bounce.Out(2*a-1)+0.5}}};
11 | TWEEN.Interpolation={Linear:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.Linear;return 0>c?f(a[0],a[1],d):1b?b:e+1],d-e)},Bezier:function(a,c){var b=0,d=a.length-1,e=Math.pow,f=TWEEN.Interpolation.Utils.Bernstein,h;for(h=0;h<=d;h++)b+=e(1-c,d-h)*e(c,h)*a[h]*f(d,h);return b},CatmullRom:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.CatmullRom;return a[0]===a[b]?(0>c&&(e=Math.floor(d=b*(1+c))),f(a[(e-
12 | 1+b)%b],a[e],a[(e+1)%b],a[(e+2)%b],d-e)):0>c?a[0]-(f(a[0],a[0],a[1],a[1],-d)-a[0]):1$v){
38 | $size[$k] = $v['size'];
39 | $time[$k] = $v['time'];
40 | $name[$k] = $v['name'];
41 | }
42 | array_multisort($time,SORT_DESC,SORT_STRING, $files);//按时间排序
43 | //array_multisort($name,SORT_DESC,SORT_STRING, $files);//按名字排序
44 | //array_multisort($size,SORT_DESC,SORT_NUMERIC, $files);//按大小排序
45 | }
46 | return $files;
47 | }
48 |
49 |
50 | static public function getFormate($url){
51 | $imageTypeArray = array
52 | (
53 | 0=>'UNKNOWN',
54 | 1=>'GIF',
55 | 2=>'JPEG',
56 | 3=>'PNG',
57 | 4=>'SWF',
58 | 5=>'PSD',
59 | 6=>'BMP',
60 | 7=>'TIFF_II',
61 | 8=>'TIFF_MM',
62 | 9=>'JPC',
63 | 10=>'JP2',
64 | 11=>'JPX',
65 | 12=>'JB2',
66 | 13=>'SWC',
67 | 14=>'IFF',
68 | 15=>'WBMP',
69 | 16=>'XBM',
70 | 17=>'ICO',
71 | 18=>'COUNT'
72 | );
73 | $size = getimagesize($url);
74 | return $imageTypeArray[$size[2]];
75 | }
76 |
77 | /**
78 | * 图片下载
79 | * @param $url
80 | * @param $dir
81 | * @return array|bool|string
82 | */
83 | static public function download($url,$dir){
84 |
85 | if(!is_dir($dir)){
86 | mkdir($dir,0775,true);
87 | }
88 | //去除URL连接上面可能的引号
89 | $url = preg_replace( '/(?:^[\'"]+|[\'"\/]+$)/', '', $url );
90 |
91 | $ext = self::getFormate($url);
92 | list($msec,$sec) = explode ( " ", microtime () );
93 | $seq = str_replace('0.','~',$msec);
94 | $filename = date('Ymd~His',$sec). "$seq.$ext";
95 |
96 | $originalFile = $dir . DIRECTORY_SEPARATOR . $filename;
97 | $res = Curlex::get($url,$originalFile);
98 | if($res === true){
99 | return $originalFile;
100 | }
101 | else{
102 | return $res;
103 | }
104 | }
105 |
106 |
107 | /**
108 | * 图片缩放
109 | * @param $src : 源图片路径
110 | * @param $dest : 目标图片路径
111 | * @param array $size : 图片的 [宽度,高度]
112 | * @return bool
113 | */
114 | static public function resize($src,$dest,$size = [120,160]){
115 |
116 | if(!file_exists($src)){
117 | return "源文件不存在: $src";
118 | }
119 |
120 | $destDir = dirname($dest);
121 | if(!is_dir($destDir))
122 | {
123 | mkdir($destDir,0775,true);
124 | }
125 |
126 | list($distWidth,$distHeight) = $size;
127 |
128 | list($width, $height) = getimagesize($src);
129 | $percent = 1;
130 | if($width /$height >= $distWidth/$distHeight)
131 | {
132 |
133 | if($width > $distWidth)
134 | {
135 | $percent = $distHeight/$height;
136 | }
137 | }
138 | else
139 | {
140 | if($height > $distHeight)
141 | {
142 | $percent = $distWidth/$width;
143 | }
144 | }
145 | $new_width = $width * $percent;
146 | $new_height = $height * $percent;
147 | //创建新的图片此图片的标志为$image_p
148 | $image_p = imagecreatetruecolor($new_width, $new_height);
149 | $image = imagecreatefromjpeg($src);
150 | imagecopyresampled($image_p, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
151 |
152 | // Output
153 | imagejpeg($image_p, $dest, 100);//quality为图片输出的质量范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大)。
154 |
155 | return true;
156 | }
157 |
158 | }
--------------------------------------------------------------------------------
/src/common/image/Photo.php:
--------------------------------------------------------------------------------
1 | $v){
38 | $size[$k] = $v['size'];
39 | $time[$k] = $v['time'];
40 | $name[$k] = $v['name'];
41 | }
42 | array_multisort($time,SORT_DESC,SORT_STRING, $files);//按时间排序
43 | //array_multisort($name,SORT_DESC,SORT_STRING, $files);//按名字排序
44 | //array_multisort($size,SORT_DESC,SORT_NUMERIC, $files);//按大小排序
45 | }
46 | return $files;
47 | }
48 |
49 |
50 | static public function getFormate($url){
51 | $imageTypeArray = array
52 | (
53 | 0=>'UNKNOWN',
54 | 1=>'GIF',
55 | 2=>'JPEG',
56 | 3=>'PNG',
57 | 4=>'SWF',
58 | 5=>'PSD',
59 | 6=>'BMP',
60 | 7=>'TIFF_II',
61 | 8=>'TIFF_MM',
62 | 9=>'JPC',
63 | 10=>'JP2',
64 | 11=>'JPX',
65 | 12=>'JB2',
66 | 13=>'SWC',
67 | 14=>'IFF',
68 | 15=>'WBMP',
69 | 16=>'XBM',
70 | 17=>'ICO',
71 | 18=>'COUNT'
72 | );
73 | $size = getimagesize($url);
74 | return $imageTypeArray[$size[2]];
75 | }
76 |
77 | /**
78 | * 图片下载
79 | * @param $url
80 | * @param $dir
81 | * @return array|bool|string
82 | */
83 | static public function download($url,$dir){
84 |
85 | if(!is_dir($dir)){
86 | mkdir($dir,0775,true);
87 | }
88 | //去除URL连接上面可能的引号
89 | $url = preg_replace( '/(?:^[\'"]+|[\'"\/]+$)/', '', $url );
90 |
91 | $ext = self::getFormate($url);
92 | list($msec,$sec) = explode ( " ", microtime () );
93 | $seq = str_replace('0.','~',$msec);
94 | $filename = date('Ymd~His',$sec). "$seq.$ext";
95 |
96 | $originalFile = $dir . DIRECTORY_SEPARATOR . $filename;
97 | $res = Curlex::get($url,$originalFile);
98 | if($res === true){
99 | return $originalFile;
100 | }
101 | else{
102 | return $res;
103 | }
104 | }
105 |
106 |
107 | /**
108 | * 图片缩放
109 | * @param $src : 源图片路径
110 | * @param $dest : 目标图片路径
111 | * @param array $size : 图片的 [宽度,高度]
112 | * @return bool
113 | */
114 | static public function resize($src,$dest,$size = [120,160]){
115 |
116 | if(!file_exists($src)){
117 | return "源文件不存在: $src";
118 | }
119 |
120 | $destDir = dirname($dest);
121 | if(!is_dir($destDir))
122 | {
123 | mkdir($destDir,0775,true);
124 | }
125 |
126 | list($distWidth,$distHeight) = $size;
127 |
128 | list($width, $height) = getimagesize($src);
129 | $percent = 1;
130 | if($width /$height >= $distWidth/$distHeight)
131 | {
132 |
133 | if($width > $distWidth)
134 | {
135 | $percent = $distHeight/$height;
136 | }
137 | }
138 | else
139 | {
140 | if($height > $distHeight)
141 | {
142 | $percent = $distWidth/$width;
143 | }
144 | }
145 | $new_width = $width * $percent;
146 | $new_height = $height * $percent;
147 | //创建新的图片此图片的标志为$image_p
148 | $image_p = imagecreatetruecolor($new_width, $new_height);
149 | $image = imagecreatefromjpeg($src);
150 | imagecopyresampled($image_p, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
151 |
152 | // Output
153 | imagejpeg($image_p, $dest, 100);//quality为图片输出的质量范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大)。
154 |
155 | return true;
156 | }
157 |
158 | }
--------------------------------------------------------------------------------
/public/js/lazyload.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Lazy Load - JavaScript plugin for lazy loading images
3 | *
4 | * Copyright (c) 2007-2017 Mika Tuupola
5 | *
6 | * Licensed under the MIT license:
7 | * http://www.opensource.org/licenses/mit-license.php
8 | *
9 | * Project home:
10 | * https://appelsiini.net/projects/lazyload
11 | *
12 | * Version: 2.0.0-beta.2
13 | *
14 | */
15 |
16 | (function (root, factory) {
17 | if (typeof exports === "object") {
18 | module.exports = factory(root);
19 | } else if (typeof define === "function" && define.amd) {
20 | define([], factory(root));
21 | } else {
22 | root.LazyLoad = factory(root);
23 | }
24 | }) (typeof global !== "undefined" ? global : this.window || this.global, function (root) {
25 |
26 | "use strict";
27 |
28 | const defaults = {
29 | src: "data-src",
30 | srcset: "data-srcset",
31 | selector: ".lazyload"
32 | };
33 |
34 | /**
35 | * Merge two or more objects. Returns a new object.
36 | * @private
37 | * @param {Boolean} deep If true, do a deep (or recursive) merge [optional]
38 | * @param {Object} objects The objects to merge together
39 | * @returns {Object} Merged values of defaults and options
40 | */
41 | const extend = function () {
42 |
43 | let extended = {};
44 | let deep = false;
45 | let i = 0;
46 | let length = arguments.length;
47 |
48 | /* Check if a deep merge */
49 | if (Object.prototype.toString.call(arguments[0]) === "[object Boolean]") {
50 | deep = arguments[0];
51 | i++;
52 | }
53 |
54 | /* Merge the object into the extended object */
55 | let merge = function (obj) {
56 | for (let prop in obj) {
57 | if (Object.prototype.hasOwnProperty.call(obj, prop)) {
58 | /* If deep merge and property is an object, merge properties */
59 | if (deep && Object.prototype.toString.call(obj[prop]) === "[object Object]") {
60 | extended[prop] = extend(true, extended[prop], obj[prop]);
61 | } else {
62 | extended[prop] = obj[prop];
63 | }
64 | }
65 | }
66 | };
67 |
68 | /* Loop through each object and conduct a merge */
69 | for (; i < length; i++) {
70 | let obj = arguments[i];
71 | merge(obj);
72 | }
73 |
74 | return extended;
75 | };
76 |
77 | function LazyLoad(images, options) {
78 | this.settings = extend(defaults, options || {});
79 | this.images = images || document.querySelectorAll(this.settings.selector);
80 | this.observer = null;
81 | this.init();
82 | }
83 |
84 | LazyLoad.prototype = {
85 | init: function() {
86 |
87 | /* Without observers load everything and bail out early. */
88 | if (!root.IntersectionObserver) {
89 | this.loadImages();
90 | return;
91 | }
92 |
93 | let self = this;
94 | let observerConfig = {
95 | root: null,
96 | rootMargin: "0px",
97 | threshold: [0]
98 | };
99 |
100 | this.observer = new IntersectionObserver(function(entries) {
101 | entries.forEach(function (entry) {
102 | if (entry.intersectionRatio > 0) {
103 | self.observer.unobserve(entry.target);
104 | let src = entry.target.getAttribute(self.settings.src);
105 | let srcset = entry.target.getAttribute(self.settings.srcset);
106 | if ("img" === entry.target.tagName.toLowerCase()) {
107 | if (src) {
108 | entry.target.src = src;
109 | }
110 | if (srcset) {
111 | entry.target.srcset = srcset;
112 | }
113 | } else {
114 | entry.target.style.backgroundImage = "url(" + src + ")";
115 | }
116 | }
117 | });
118 | }, observerConfig);
119 |
120 | this.images.forEach(function (image) {
121 | self.observer.observe(image);
122 | });
123 | },
124 |
125 | loadAndDestroy: function () {
126 | if (!this.settings) { return; }
127 | this.loadImages();
128 | this.destroy();
129 | },
130 |
131 | loadImages: function () {
132 | if (!this.settings) { return; }
133 |
134 | let self = this;
135 | this.images.forEach(function (image) {
136 | let src = image.getAttribute(self.settings.src);
137 | let srcset = image.getAttribute(self.settings.srcset);
138 | if ("img" === image.tagName.toLowerCase()) {
139 | if (src) {
140 | image.src = src;
141 | }
142 | if (srcset) {
143 | image.srcset = srcset;
144 | }
145 | } else {
146 | image.style.backgroundImage = "url(" + src + ")";
147 | }
148 | });
149 | },
150 |
151 | destroy: function () {
152 | if (!this.settings) { return; }
153 | this.observer.disconnect();
154 | this.settings = null;
155 | }
156 | };
157 |
158 | root.lazyload = function(images, options) {
159 | return new LazyLoad(images, options);
160 | };
161 |
162 | if (root.jQuery) {
163 | const $ = root.jQuery;
164 | $.fn.lazyload = function (options) {
165 | options = options || {};
166 | options.attribute = options.attribute || "data-src";
167 | new LazyLoad($.makeArray(this), options);
168 | return this;
169 | };
170 | }
171 |
172 | return LazyLoad;
173 | });
174 |
--------------------------------------------------------------------------------
/public/wall2/CSS3DRenderer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs
3 | * @author mrdoob / http://mrdoob.com/
4 | */
5 |
6 | THREE.CSS3DObject = function ( element ) {
7 |
8 | THREE.Object3D.call( this );
9 |
10 | this.element = element;
11 | this.element.style.position = 'absolute';
12 |
13 | this.addEventListener( 'removed', function ( event ) {
14 |
15 | if ( this.element.parentNode !== null ) {
16 |
17 | this.element.parentNode.removeChild( this.element );
18 |
19 | for ( var i = 0, l = this.children.length; i < l; i ++ ) {
20 |
21 | this.children[ i ].dispatchEvent( event );
22 |
23 | }
24 |
25 | }
26 |
27 | } );
28 |
29 | };
30 |
31 | THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype );
32 |
33 | THREE.CSS3DSprite = function ( element ) {
34 |
35 | THREE.CSS3DObject.call( this, element );
36 |
37 | };
38 |
39 | THREE.CSS3DSprite.prototype = Object.create( THREE.CSS3DObject.prototype );
40 |
41 | //
42 |
43 | THREE.CSS3DRenderer = function () {
44 |
45 | console.log( 'THREE.CSS3DRenderer', THREE.REVISION );
46 |
47 | var _width, _height;
48 | var _widthHalf, _heightHalf;
49 |
50 | var matrix = new THREE.Matrix4();
51 |
52 | var domElement = document.createElement( 'div' );
53 | domElement.style.overflow = 'hidden';
54 |
55 | domElement.style.WebkitTransformStyle = 'preserve-3d';
56 | domElement.style.MozTransformStyle = 'preserve-3d';
57 | domElement.style.oTransformStyle = 'preserve-3d';
58 | domElement.style.transformStyle = 'preserve-3d';
59 |
60 | this.domElement = domElement;
61 |
62 | var cameraElement = document.createElement( 'div' );
63 |
64 | cameraElement.style.WebkitTransformStyle = 'preserve-3d';
65 | cameraElement.style.MozTransformStyle = 'preserve-3d';
66 | cameraElement.style.oTransformStyle = 'preserve-3d';
67 | cameraElement.style.transformStyle = 'preserve-3d';
68 |
69 | domElement.appendChild( cameraElement );
70 |
71 | this.setClearColor = function () {
72 |
73 | };
74 |
75 | this.setSize = function ( width, height ) {
76 |
77 | _width = width;
78 | _height = height;
79 |
80 | _widthHalf = _width / 2;
81 | _heightHalf = _height / 2;
82 |
83 | domElement.style.width = width + 'px';
84 | domElement.style.height = height + 'px';
85 |
86 | cameraElement.style.width = width + 'px';
87 | cameraElement.style.height = height + 'px';
88 |
89 | };
90 |
91 | var epsilon = function ( value ) {
92 |
93 | return Math.abs( value ) < 0.000001 ? 0 : value;
94 |
95 | };
96 |
97 | var getCameraCSSMatrix = function ( matrix ) {
98 |
99 | var elements = matrix.elements;
100 |
101 | return 'matrix3d(' +
102 | epsilon( elements[ 0 ] ) + ',' +
103 | epsilon( - elements[ 1 ] ) + ',' +
104 | epsilon( elements[ 2 ] ) + ',' +
105 | epsilon( elements[ 3 ] ) + ',' +
106 | epsilon( elements[ 4 ] ) + ',' +
107 | epsilon( - elements[ 5 ] ) + ',' +
108 | epsilon( elements[ 6 ] ) + ',' +
109 | epsilon( elements[ 7 ] ) + ',' +
110 | epsilon( elements[ 8 ] ) + ',' +
111 | epsilon( - elements[ 9 ] ) + ',' +
112 | epsilon( elements[ 10 ] ) + ',' +
113 | epsilon( elements[ 11 ] ) + ',' +
114 | epsilon( elements[ 12 ] ) + ',' +
115 | epsilon( - elements[ 13 ] ) + ',' +
116 | epsilon( elements[ 14 ] ) + ',' +
117 | epsilon( elements[ 15 ] ) +
118 | ')';
119 |
120 | };
121 |
122 | var getObjectCSSMatrix = function ( matrix ) {
123 |
124 | var elements = matrix.elements;
125 |
126 | return 'translate3d(-50%,-50%,0) matrix3d(' +
127 | epsilon( elements[ 0 ] ) + ',' +
128 | epsilon( elements[ 1 ] ) + ',' +
129 | epsilon( elements[ 2 ] ) + ',' +
130 | epsilon( elements[ 3 ] ) + ',' +
131 | epsilon( - elements[ 4 ] ) + ',' +
132 | epsilon( - elements[ 5 ] ) + ',' +
133 | epsilon( - elements[ 6 ] ) + ',' +
134 | epsilon( - elements[ 7 ] ) + ',' +
135 | epsilon( elements[ 8 ] ) + ',' +
136 | epsilon( elements[ 9 ] ) + ',' +
137 | epsilon( elements[ 10 ] ) + ',' +
138 | epsilon( elements[ 11 ] ) + ',' +
139 | epsilon( elements[ 12 ] ) + ',' +
140 | epsilon( elements[ 13 ] ) + ',' +
141 | epsilon( elements[ 14 ] ) + ',' +
142 | epsilon( elements[ 15 ] ) +
143 | ')';
144 |
145 | };
146 |
147 | var renderObject = function ( object, camera ) {
148 |
149 | if ( object instanceof THREE.CSS3DObject ) {
150 |
151 | var style;
152 |
153 | if ( object instanceof THREE.CSS3DSprite ) {
154 |
155 | // http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/
156 |
157 | matrix.copy( camera.matrixWorldInverse );
158 | matrix.transpose();
159 | matrix.copyPosition( object.matrixWorld );
160 | matrix.scale( object.scale );
161 |
162 | matrix.elements[ 3 ] = 0;
163 | matrix.elements[ 7 ] = 0;
164 | matrix.elements[ 11 ] = 0;
165 | matrix.elements[ 15 ] = 1;
166 |
167 | style = getObjectCSSMatrix( matrix );
168 |
169 | } else {
170 |
171 | style = getObjectCSSMatrix( object.matrixWorld );
172 |
173 | }
174 |
175 | var element = object.element;
176 |
177 | element.style.WebkitTransform = style;
178 | element.style.MozTransform = style;
179 | element.style.oTransform = style;
180 | element.style.transform = style;
181 |
182 | if ( element.parentNode !== cameraElement ) {
183 |
184 | cameraElement.appendChild( element );
185 |
186 | }
187 |
188 | }
189 |
190 | for ( var i = 0, l = object.children.length; i < l; i ++ ) {
191 |
192 | renderObject( object.children[ i ], camera );
193 |
194 | }
195 |
196 | };
197 |
198 | this.render = function ( scene, camera ) {
199 |
200 | var fov = 0.5 / Math.tan( THREE.Math.degToRad( camera.fov * 0.5 ) ) * _height;
201 |
202 | domElement.style.WebkitPerspective = fov + "px";
203 | domElement.style.MozPerspective = fov + "px";
204 | domElement.style.oPerspective = fov + "px";
205 | domElement.style.perspective = fov + "px";
206 |
207 | scene.updateMatrixWorld();
208 |
209 | if ( camera.parent === undefined ) camera.updateMatrixWorld();
210 |
211 | camera.matrixWorldInverse.getInverse( camera.matrixWorld );
212 |
213 | var style = "translate3d(0,0," + fov + "px)" + getCameraCSSMatrix( camera.matrixWorldInverse ) +
214 | " translate3d(" + _widthHalf + "px," + _heightHalf + "px, 0)";
215 |
216 | cameraElement.style.WebkitTransform = style;
217 | cameraElement.style.MozTransform = style;
218 | cameraElement.style.oTransform = style;
219 | cameraElement.style.transform = style;
220 |
221 | renderObject( scene, camera );
222 |
223 | };
224 | };
--------------------------------------------------------------------------------
/src/controllers/Weixin/IndexController.php:
--------------------------------------------------------------------------------
1 | container = $container;
30 | $this->cfg = $container->get("settings")['weixin'];
31 | $this->db = $container->get("db");
32 | $this->resource = $container->get("settings")['resource'];
33 | }
34 |
35 | public function vaild($req,$res,$args){
36 | $app = Factory::officialAccount($this->cfg);
37 | $response = $app->server->serve();
38 | // 将响应输出
39 | return $response->send();
40 | }
41 |
42 | public function index($req,$res,$args){
43 | // 使用配置来初始化一个公众号应用实例。
44 | $app = Factory::officialAccount($this->cfg);
45 |
46 | $app->server->push(function ($message) {
47 | $user = $message['FromUserName'];
48 | $mUser = new User($this->db);
49 | $uid = $mUser->getUidByOpenid($user);
50 | $userCenter = "http://" . $_SERVER['HTTP_HOST'] . "/wall" .DIRECTORY_SEPARATOR .$uid;
51 |
52 | $mMsg = new Message($this->db);
53 |
54 |
55 | switch ( $type = $message['MsgType']) {
56 | case 'event':
57 | if($message['Event'] == "subscribe"){
58 | return '欢迎来到拍拍客,请直接发送图片,即可看到自己的照片墙!
输入:"我的"或者"mine"可以看到自己的照片墙地址';
59 | }
60 | elseif($message['Event'] == "unsubscribe"){
61 | return '欢迎再次光临,祝您生活愉快!';
62 | }
63 |
64 | return '收到事件消息'.$message['Event'];
65 | break;
66 | case 'text':
67 | if ($message['Content'] == "mine" || $message['Content'] == "我的"){
68 | return $this->response($userCenter, "" ,"点击进入您的照片墙.或者在浏览器打开: $userCenter", "您的拍拍客地址");
69 | }
70 |
71 | return '收到文字消息:' . $message['Content'];
72 | break;
73 | case 'image':
74 | $url = $message['PicUrl'];
75 |
76 | $this->container->logger->info("收到图片:$url");
77 |
78 | $imageName = $this->image($url, $uid);
79 | if($imageName === false){
80 | return $this->response($url, $url ,$user, "图片操作失败");
81 | }
82 | $this->container->logger->info("转码成功,$imageName");
83 |
84 | if($qnUrl = $this->upQiniu($url,"pper_uid$uid"."_".$imageName)){ // 上传到七牛云
85 | $orgImage = $qnUrl;
86 | $this->container->logger->info("七牛云上传成功,$orgImage");
87 | //删除本地图片
88 | unlink($this->resource['path'] . DIRECTORY_SEPARATOR . "$uid/original/$imageName"); // 删除本地原图
89 | $this->container->logger->info("删除本地成功,".$this->resource['path'] . DIRECTORY_SEPARATOR . "$uid/original/$imageName");
90 | }
91 | else{
92 | $orgImage = $this->resource['url'] . DIRECTORY_SEPARATOR . "$uid/original/$imageName";
93 | $this->container->logger->info("上传失败,$orgImage");
94 | }
95 |
96 | $resize = $this->resource['url'] . DIRECTORY_SEPARATOR . "$uid/resize/$imageName";
97 | $msg = $mMsg->saveMessage($uid,$type,$resize,$orgImage);
98 | $this->container->logger->info("数据库更新失败,$msg");
99 |
100 | return $this->response($userCenter, $resize,'图片已经上墙,点击或者刷新可以查看最新的照片墙','上墙通知');
101 |
102 | break;
103 | case 'voice':
104 | return '收到语音消息';
105 | break;
106 | case 'video':
107 | return '收到视频消息';
108 | break;
109 | case 'location':
110 | return '收到坐标消息';
111 | break;
112 | case 'link':
113 | return '收到链接消息';
114 | break;
115 | case 'file':
116 | return '收到文件消息';
117 | // ... 其它消息
118 | default:
119 | return '收到其它消息';
120 | break;
121 | }
122 |
123 | // ...
124 | });
125 | $response = $app->server->serve();
126 |
127 | // 将响应输出
128 | return $response->send();
129 | }
130 |
131 |
132 | /**
133 | * 测试代码使用
134 | * @param $request
135 | * @param $response
136 | * @param $args
137 | */
138 | public function ttt($request, $response, $args){
139 | set_error_handler(function ($errno, $errstr) {
140 | // do something
141 | echo "\r\n~~~~~找到错误 :-D~~~~~\r\n";
142 | echo "错误信息:($errno)$errstr \r\n";
143 | debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
144 | echo "\r\n";
145 | }, E_WARNING);
146 |
147 | $url = 'http://mmbiz.qpic.cn/mmbiz_jpg/EuJU30O3lFKeOmC254TagSVpKL1h4ibSA8lLQXeia4TrVo6F3s7mPQxBvJia9XNeMbmmWtVBkeib7ibicJU34mlMY6hw/0';
148 | $openId = 'os_G_jqeNPMfWLA-A2KIKodfm3SY';
149 |
150 | $mUser = new User($this->db);
151 |
152 | $uid = $mUser->getUid($openId);
153 |
154 | $userCenter = "http://".$_SERVER['HTTP_HOST'] . "/users/$uid";
155 | // return $this->response($url,$userCenter);
156 |
157 | //$this->image($url,$uid);
158 |
159 | $imageName = $this->image($url, $uid);
160 | if($imageName === false){
161 | return $this->response($url, $url ,$openId, "图片操作失败");
162 | }
163 |
164 | if($qnUrl = $this->upQiniu($url,"pper_uid$uid"."_".$imageName)){ // 上传到七牛云
165 | unlink($this->resource['path'] . DIRECTORY_SEPARATOR . "$uid/original/$imageName"); // 删除本地原图
166 | $orgImage = $qnUrl;
167 |
168 | }
169 | else{
170 | $orgImage = $this->resource['url'] . DIRECTORY_SEPARATOR . "$uid/original/$imageName";
171 | }
172 |
173 | restore_error_handler();
174 | //echo $orgImage;
175 | //*/
176 | }
177 |
178 |
179 |
180 | private function upQiniu($src,$name){
181 | $cfg = $this->container->get("settings")['qiniu'];
182 | if(!$cfg['enable']){
183 | return false;
184 | }
185 | $qn = new Qiniu($cfg);
186 | $url = $qn->upload($src,$name);
187 | return $url;
188 | }
189 |
190 |
191 | private function image($url,$uid){
192 | $dir = $this->container->get("settings")['resource'];
193 | $originalDir = $dir['path'] . DIRECTORY_SEPARATOR . $uid . DIRECTORY_SEPARATOR . "original";
194 | $resizeDir = $dir['path'] . DIRECTORY_SEPARATOR . $uid . DIRECTORY_SEPARATOR . "resize";
195 | $img = Photo::download($url,$originalDir);
196 | $imageName = basename($img);
197 | if(file_exists($img)){
198 | $resieFile = $resizeDir.DIRECTORY_SEPARATOR.$imageName;
199 | if(Photo::resize($img,$resizeDir.DIRECTORY_SEPARATOR.$imageName) !== true){
200 | $this->container->logger->error("生成缩略失败:$resieFile");
201 | return false;
202 | }
203 | else {
204 | return $imageName;
205 | }
206 | }
207 | else{
208 | $this->container->logger->error("图片下载失败:$url\r\n目标位置: $img");
209 | return false;
210 | }
211 | }
212 |
213 | private function response($url,$image,$desc,$title='拍拍客')
214 | {
215 | $items = [
216 | new NewsItem([
217 | 'title' => $title,
218 | 'description' => $desc,
219 | 'url' => $url,
220 | 'image' => $image,
221 | // ...
222 | ]),
223 | ];
224 | return $news = new News($items);
225 | }
226 |
227 | }
--------------------------------------------------------------------------------
/public/js/CSS3DRenderer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs
3 | * @author mrdoob / http://mrdoob.com/
4 | * @author yomotsu / https://yomotsu.net/
5 | */
6 |
7 | THREE.CSS3DObject = function ( element ) {
8 |
9 | THREE.Object3D.call( this );
10 |
11 | this.element = element;
12 | this.element.style.position = 'absolute';
13 |
14 | this.addEventListener( 'removed', function () {
15 |
16 | if ( this.element.parentNode !== null ) {
17 |
18 | this.element.parentNode.removeChild( this.element );
19 |
20 | }
21 |
22 | } );
23 |
24 | };
25 |
26 | THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype );
27 | THREE.CSS3DObject.prototype.constructor = THREE.CSS3DObject;
28 |
29 | THREE.CSS3DSprite = function ( element ) {
30 |
31 | THREE.CSS3DObject.call( this, element );
32 |
33 | };
34 |
35 | THREE.CSS3DSprite.prototype = Object.create( THREE.CSS3DObject.prototype );
36 | THREE.CSS3DSprite.prototype.constructor = THREE.CSS3DSprite;
37 |
38 | //
39 |
40 | THREE.CSS3DRenderer = function () {
41 |
42 | console.log( 'THREE.CSS3DRenderer', THREE.REVISION );
43 |
44 | var _width, _height;
45 | var _widthHalf, _heightHalf;
46 |
47 | var matrix = new THREE.Matrix4();
48 |
49 | var cache = {
50 | camera: { fov: 0, style: '' },
51 | objects: new WeakMap()
52 | };
53 |
54 | var domElement = document.createElement( 'div' );
55 | domElement.style.overflow = 'hidden';
56 |
57 | this.domElement = domElement;
58 |
59 | var cameraElement = document.createElement( 'div' );
60 |
61 | cameraElement.style.WebkitTransformStyle = 'preserve-3d';
62 | cameraElement.style.transformStyle = 'preserve-3d';
63 |
64 | domElement.appendChild( cameraElement );
65 |
66 | var isIE = /Trident/i.test( navigator.userAgent );
67 |
68 | this.getSize = function () {
69 |
70 | return {
71 | width: _width,
72 | height: _height
73 | };
74 |
75 | };
76 |
77 | this.setSize = function ( width, height ) {
78 |
79 | _width = width;
80 | _height = height;
81 | _widthHalf = _width / 2;
82 | _heightHalf = _height / 2;
83 |
84 | domElement.style.width = width + 'px';
85 | domElement.style.height = height + 'px';
86 |
87 | cameraElement.style.width = width + 'px';
88 | cameraElement.style.height = height + 'px';
89 |
90 | };
91 |
92 | function epsilon( value ) {
93 |
94 | return Math.abs( value ) < 1e-10 ? 0 : value;
95 |
96 | }
97 |
98 | function getCameraCSSMatrix( matrix ) {
99 |
100 | var elements = matrix.elements;
101 |
102 | return 'matrix3d(' +
103 | epsilon( elements[ 0 ] ) + ',' +
104 | epsilon( - elements[ 1 ] ) + ',' +
105 | epsilon( elements[ 2 ] ) + ',' +
106 | epsilon( elements[ 3 ] ) + ',' +
107 | epsilon( elements[ 4 ] ) + ',' +
108 | epsilon( - elements[ 5 ] ) + ',' +
109 | epsilon( elements[ 6 ] ) + ',' +
110 | epsilon( elements[ 7 ] ) + ',' +
111 | epsilon( elements[ 8 ] ) + ',' +
112 | epsilon( - elements[ 9 ] ) + ',' +
113 | epsilon( elements[ 10 ] ) + ',' +
114 | epsilon( elements[ 11 ] ) + ',' +
115 | epsilon( elements[ 12 ] ) + ',' +
116 | epsilon( - elements[ 13 ] ) + ',' +
117 | epsilon( elements[ 14 ] ) + ',' +
118 | epsilon( elements[ 15 ] ) +
119 | ')';
120 |
121 | }
122 |
123 | function getObjectCSSMatrix( matrix, cameraCSSMatrix ) {
124 |
125 | var elements = matrix.elements;
126 | var matrix3d = 'matrix3d(' +
127 | epsilon( elements[ 0 ] ) + ',' +
128 | epsilon( elements[ 1 ] ) + ',' +
129 | epsilon( elements[ 2 ] ) + ',' +
130 | epsilon( elements[ 3 ] ) + ',' +
131 | epsilon( - elements[ 4 ] ) + ',' +
132 | epsilon( - elements[ 5 ] ) + ',' +
133 | epsilon( - elements[ 6 ] ) + ',' +
134 | epsilon( - elements[ 7 ] ) + ',' +
135 | epsilon( elements[ 8 ] ) + ',' +
136 | epsilon( elements[ 9 ] ) + ',' +
137 | epsilon( elements[ 10 ] ) + ',' +
138 | epsilon( elements[ 11 ] ) + ',' +
139 | epsilon( elements[ 12 ] ) + ',' +
140 | epsilon( elements[ 13 ] ) + ',' +
141 | epsilon( elements[ 14 ] ) + ',' +
142 | epsilon( elements[ 15 ] ) +
143 | ')';
144 |
145 | if ( isIE ) {
146 |
147 | return 'translate(-50%,-50%)' +
148 | 'translate(' + _widthHalf + 'px,' + _heightHalf + 'px)' +
149 | cameraCSSMatrix +
150 | matrix3d;
151 |
152 | }
153 |
154 | return 'translate(-50%,-50%)' + matrix3d;
155 |
156 | }
157 |
158 | function renderObject( object, camera, cameraCSSMatrix ) {
159 |
160 | if ( object instanceof THREE.CSS3DObject ) {
161 |
162 | var style;
163 |
164 | if ( object instanceof THREE.CSS3DSprite ) {
165 |
166 | // http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/
167 |
168 | matrix.copy( camera.matrixWorldInverse );
169 | matrix.transpose();
170 | matrix.copyPosition( object.matrixWorld );
171 | matrix.scale( object.scale );
172 |
173 | matrix.elements[ 3 ] = 0;
174 | matrix.elements[ 7 ] = 0;
175 | matrix.elements[ 11 ] = 0;
176 | matrix.elements[ 15 ] = 1;
177 |
178 | style = getObjectCSSMatrix( matrix, cameraCSSMatrix );
179 |
180 | } else {
181 |
182 | style = getObjectCSSMatrix( object.matrixWorld, cameraCSSMatrix );
183 |
184 | }
185 |
186 | var element = object.element;
187 | var cachedObject = cache.objects.get( object );
188 |
189 | if ( cachedObject === undefined || cachedObject.style !== style ) {
190 |
191 | element.style.WebkitTransform = style;
192 | element.style.transform = style;
193 |
194 | var objectData = { style: style };
195 |
196 | if ( isIE ) {
197 |
198 | objectData.distanceToCameraSquared = getDistanceToSquared( camera, object );
199 |
200 | }
201 |
202 | cache.objects.set( object, objectData );
203 |
204 | }
205 |
206 | if ( element.parentNode !== cameraElement ) {
207 |
208 | cameraElement.appendChild( element );
209 |
210 | }
211 |
212 | }
213 |
214 | for ( var i = 0, l = object.children.length; i < l; i ++ ) {
215 |
216 | renderObject( object.children[ i ], camera, cameraCSSMatrix );
217 |
218 | }
219 |
220 | }
221 |
222 | var getDistanceToSquared = function () {
223 |
224 | var a = new THREE.Vector3();
225 | var b = new THREE.Vector3();
226 |
227 | return function ( object1, object2 ) {
228 |
229 | a.setFromMatrixPosition( object1.matrixWorld );
230 | b.setFromMatrixPosition( object2.matrixWorld );
231 |
232 | return a.distanceToSquared( b );
233 |
234 | };
235 |
236 | }();
237 |
238 | function filterAndFlatten( scene ) {
239 |
240 | var result = [];
241 |
242 | scene.traverse( function ( object ) {
243 |
244 | if ( object instanceof THREE.CSS3DObject ) result.push( object );
245 |
246 | } );
247 |
248 | return result;
249 |
250 | }
251 |
252 | function zOrder( scene ) {
253 |
254 | var sorted = filterAndFlatten( scene ).sort( function ( a, b ) {
255 |
256 | var distanceA = cache.objects.get( a ).distanceToCameraSquared;
257 | var distanceB = cache.objects.get( b ).distanceToCameraSquared;
258 |
259 | return distanceA - distanceB;
260 |
261 | } );
262 |
263 | var zMax = sorted.length;
264 |
265 | for ( var i = 0, l = sorted.length; i < l; i ++ ) {
266 |
267 | sorted[ i ].element.style.zIndex = zMax - i;
268 |
269 | }
270 |
271 | }
272 |
273 | this.render = function ( scene, camera ) {
274 |
275 | var fov = camera.projectionMatrix.elements[ 5 ] * _heightHalf;
276 |
277 | if ( cache.camera.fov !== fov ) {
278 |
279 | if ( camera.isPerspectiveCamera ) {
280 |
281 | domElement.style.WebkitPerspective = fov + 'px';
282 | domElement.style.perspective = fov + 'px';
283 |
284 | }
285 |
286 | cache.camera.fov = fov;
287 |
288 | }
289 |
290 | scene.updateMatrixWorld();
291 |
292 | if ( camera.parent === null ) camera.updateMatrixWorld();
293 |
294 | if ( camera.isOrthographicCamera ) {
295 |
296 | var tx = - ( camera.right + camera.left ) / 2;
297 | var ty = ( camera.top + camera.bottom ) / 2;
298 |
299 | }
300 |
301 | var cameraCSSMatrix = camera.isOrthographicCamera ?
302 | 'scale(' + fov + ')' + 'translate(' + epsilon( tx ) + 'px,' + epsilon( ty ) + 'px)' + getCameraCSSMatrix( camera.matrixWorldInverse ) :
303 | 'translateZ(' + fov + 'px)' + getCameraCSSMatrix( camera.matrixWorldInverse );
304 |
305 | var style = cameraCSSMatrix +
306 | 'translate(' + _widthHalf + 'px,' + _heightHalf + 'px)';
307 |
308 | if ( cache.camera.style !== style && ! isIE ) {
309 |
310 | cameraElement.style.WebkitTransform = style;
311 | cameraElement.style.transform = style;
312 |
313 | cache.camera.style = style;
314 |
315 | }
316 |
317 | renderObject( scene, camera, cameraCSSMatrix );
318 |
319 | if ( isIE ) {
320 |
321 | // IE10 and 11 does not support 'preserve-3d'.
322 | // Thus, z-order in 3D will not work.
323 | // We have to calc z-order manually and set CSS z-index for IE.
324 | // FYI: z-index can't handle object intersection
325 | zOrder( scene );
326 |
327 | }
328 |
329 | };
330 |
331 | };
332 |
--------------------------------------------------------------------------------
/public/js/lightbox.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Lightbox v2.10.0
3 | * by Lokesh Dhakar
4 | *
5 | * More info:
6 | * http://lokeshdhakar.com/projects/lightbox2/
7 | *
8 | * Copyright 2007, 2018 Lokesh Dhakar
9 | * Released under the MIT license
10 | * https://github.com/lokesh/lightbox2/blob/master/LICENSE
11 | *
12 | * @preserve
13 | */
14 | !function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?module.exports=b(require("jquery")):a.lightbox=b(a.jQuery)}(this,function(a){function b(b){this.album=[],this.currentImageIndex=void 0,this.init(),this.options=a.extend({},this.constructor.defaults),this.option(b)}return b.defaults={albumLabel:"Image %1 of %2",alwaysShowNavOnTouchDevices:!1,fadeDuration:600,fitImagesInViewport:!0,imageFadeDuration:600,positionFromTop:50,resizeDuration:700,showImageNumberLabel:!0,wrapAround:!1,disableScrolling:!1,sanitizeTitle:!1},b.prototype.option=function(b){a.extend(this.options,b)},b.prototype.imageCountLabel=function(a,b){return this.options.albumLabel.replace(/%1/g,a).replace(/%2/g,b)},b.prototype.init=function(){var b=this;a(document).ready(function(){b.enable(),b.build()})},b.prototype.enable=function(){var b=this;a("body").on("click","a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]",function(c){return b.start(a(c.currentTarget)),!1})},b.prototype.build=function(){if(!(a("#lightbox").length>0)){var b=this;a('').appendTo(a("body")),this.$lightbox=a("#lightbox"),this.$overlay=a("#lightboxOverlay"),this.$outerContainer=this.$lightbox.find(".lb-outerContainer"),this.$container=this.$lightbox.find(".lb-container"),this.$image=this.$lightbox.find(".lb-image"),this.$nav=this.$lightbox.find(".lb-nav"),this.containerPadding={top:parseInt(this.$container.css("padding-top"),10),right:parseInt(this.$container.css("padding-right"),10),bottom:parseInt(this.$container.css("padding-bottom"),10),left:parseInt(this.$container.css("padding-left"),10)},this.imageBorderWidth={top:parseInt(this.$image.css("border-top-width"),10),right:parseInt(this.$image.css("border-right-width"),10),bottom:parseInt(this.$image.css("border-bottom-width"),10),left:parseInt(this.$image.css("border-left-width"),10)},this.$overlay.hide().on("click",function(){return b.end(),!1}),this.$lightbox.hide().on("click",function(c){return"lightbox"===a(c.target).attr("id")&&b.end(),!1}),this.$outerContainer.on("click",function(c){return"lightbox"===a(c.target).attr("id")&&b.end(),!1}),this.$lightbox.find(".lb-prev").on("click",function(){return 0===b.currentImageIndex?b.changeImage(b.album.length-1):b.changeImage(b.currentImageIndex-1),!1}),this.$lightbox.find(".lb-next").on("click",function(){return b.currentImageIndex===b.album.length-1?b.changeImage(0):b.changeImage(b.currentImageIndex+1),!1}),this.$nav.on("mousedown",function(a){3===a.which&&(b.$nav.css("pointer-events","none"),b.$lightbox.one("contextmenu",function(){setTimeout(function(){this.$nav.css("pointer-events","auto")}.bind(b),0)}))}),this.$lightbox.find(".lb-loader, .lb-close").on("click",function(){return b.end(),!1})}},b.prototype.start=function(b){function c(a){d.album.push({alt:a.attr("data-alt"),link:a.attr("href"),title:a.attr("data-title")||a.attr("title")})}var d=this,e=a(window);e.on("resize",a.proxy(this.sizeOverlay,this)),a("select, object, embed").css({visibility:"hidden"}),this.sizeOverlay(),this.album=[];var f,g=0,h=b.attr("data-lightbox");if(h){f=a(b.prop("tagName")+'[data-lightbox="'+h+'"]');for(var i=0;ii||e.height>h)&&(e.width/i>e.height/h?(g=i,f=parseInt(e.height/(e.width/g),10),d.width(g),d.height(f)):(f=h,g=parseInt(e.width/(e.height/f),10),d.width(g),d.height(f)))),c.sizeContainer(d.width(),d.height())},e.src=this.album[b].link,this.currentImageIndex=b},b.prototype.sizeOverlay=function(){this.$overlay.width(a(document).width()).height(a(document).height())},b.prototype.sizeContainer=function(a,b){function c(){d.$lightbox.find(".lb-dataContainer").width(g),d.$lightbox.find(".lb-prevLink").height(h),d.$lightbox.find(".lb-nextLink").height(h),d.showImage()}var d=this,e=this.$outerContainer.outerWidth(),f=this.$outerContainer.outerHeight(),g=a+this.containerPadding.left+this.containerPadding.right+this.imageBorderWidth.left+this.imageBorderWidth.right,h=b+this.containerPadding.top+this.containerPadding.bottom+this.imageBorderWidth.top+this.imageBorderWidth.bottom;e!==g||f!==h?this.$outerContainer.animate({width:g,height:h},this.options.resizeDuration,"swing",function(){c()}):c()},b.prototype.showImage=function(){this.$lightbox.find(".lb-loader").stop(!0).hide(),this.$lightbox.find(".lb-image").fadeIn(this.options.imageFadeDuration),this.updateNav(),this.updateDetails(),this.preloadNeighboringImages(),this.enableKeyboardNav()},b.prototype.updateNav=function(){var a=!1;try{document.createEvent("TouchEvent"),a=!!this.options.alwaysShowNavOnTouchDevices}catch(a){}this.$lightbox.find(".lb-nav").show(),this.album.length>1&&(this.options.wrapAround?(a&&this.$lightbox.find(".lb-prev, .lb-next").css("opacity","1"),this.$lightbox.find(".lb-prev, .lb-next").show()):(this.currentImageIndex>0&&(this.$lightbox.find(".lb-prev").show(),a&&this.$lightbox.find(".lb-prev").css("opacity","1")),this.currentImageIndex1&&this.options.showImageNumberLabel){var d=this.imageCountLabel(this.currentImageIndex+1,this.album.length);this.$lightbox.find(".lb-number").text(d).fadeIn("fast")}else this.$lightbox.find(".lb-number").hide();this.$outerContainer.removeClass("animating"),this.$lightbox.find(".lb-dataContainer").fadeIn(this.options.resizeDuration,function(){return b.sizeOverlay()})},b.prototype.preloadNeighboringImages=function(){if(this.album.length>this.currentImageIndex+1){(new Image).src=this.album[this.currentImageIndex+1].link}if(this.currentImageIndex>0){(new Image).src=this.album[this.currentImageIndex-1].link}},b.prototype.enableKeyboardNav=function(){a(document).on("keyup.keyboard",a.proxy(this.keyboardAction,this))},b.prototype.disableKeyboardNav=function(){a(document).off(".keyboard")},b.prototype.keyboardAction=function(a){var b=a.keyCode,c=String.fromCharCode(b).toLowerCase();27===b||c.match(/x|o|c/)?this.end():"p"===c||37===b?0!==this.currentImageIndex?this.changeImage(this.currentImageIndex-1):this.options.wrapAround&&this.album.length>1&&this.changeImage(this.album.length-1):"n"!==c&&39!==b||(this.currentImageIndex!==this.album.length-1?this.changeImage(this.currentImageIndex+1):this.options.wrapAround&&this.album.length>1&&this.changeImage(0))},b.prototype.end=function(){this.disableKeyboardNav(),a(window).off("resize",this.sizeOverlay),this.$lightbox.fadeOut(this.options.fadeDuration),this.$overlay.fadeOut(this.options.fadeDuration),a("select, object, embed").css({visibility:"visible"}),this.options.disableScrolling&&a("html").removeClass("lb-disable-scrolling")},new b});
15 | //# sourceMappingURL=lightbox.min.map
--------------------------------------------------------------------------------
/public/js/wall.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by liuligang on 2019/1/2.
3 | */
4 |
5 | //建议值[16,9]
6 | var table = [];
7 | var maxCol = 16,maxRow=9;
8 |
9 | var camera, scene, renderer;
10 | var controls;
11 | var radius = 100, theta = 0;
12 |
13 | var objects = [];
14 | var targets = { table: [], sphere: [], helix: [], grid: [] };
15 |
16 |
17 | function initImg() {
18 | table.forEach(function(val,index){
19 |
20 | var element = document.createElement( 'div' );
21 | element.className = 'element';
22 | element.style.backgroundColor = 'rgba(0,127,127,' + ( Math.random() * 0.5 + 0.25 ) + ')';
23 |
24 | var a = document.createElement('a');
25 | a.href = val.resource;
26 | a.setAttribute('data-lightbox','roadtrip');
27 | a.setAttribute('title',a.href.substr(a.href.lastIndexOf('/')+1));
28 |
29 | var image=new Image();
30 | image.src=val.src;
31 | image.onload = function() {
32 | //scene.add( object );
33 | };
34 | //a.appendChild(image)
35 |
36 | element.appendChild( image );
37 | // element.setAttribute('style',"background-image: url(" + val.src + ")");
38 | a.appendChild(element);
39 |
40 | //var object = new THREE.CSS3DObject( element );
41 | var object = new THREE.CSS3DObject( a );
42 | object.position.x = Math.random() * 4000 - 2000;
43 | object.position.y = Math.random() * 4000 - 2000;
44 | object.position.z = Math.random() * 4000 - 2000;
45 | scene.add( object );
46 |
47 | objects.push( object );
48 |
49 | });
50 | }
51 |
52 |
53 | function init() {
54 |
55 | camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
56 | camera.position.z = 3000;
57 |
58 | scene = new THREE.Scene();
59 |
60 | var col = 1, row = 1;
61 |
62 |
63 | initImg();
64 |
65 |
66 |
67 | //table
68 |
69 | for ( var i = 0, l = objects.length; i < l; i ++ ) {
70 | var object = new THREE.Object3D();
71 | object.position.x = (col++ * 181 ) - 1330;
72 | object.position.y = - (row * 120 ) + 990;
73 | if(col > maxCol){
74 | col = 1;
75 | row ++;
76 | }
77 | targets.table.push( object );
78 | }
79 |
80 |
81 |
82 | // sphere
83 |
84 | var vector = new THREE.Vector3();
85 |
86 | for ( var i = 0, l = objects.length; i < l; i ++ ) {
87 |
88 | var phi = Math.acos( -1 + ( 2 * i ) / l );
89 | var theta = Math.sqrt( l * Math.PI ) * phi;
90 |
91 | var object = new THREE.Object3D();
92 |
93 | object.position.x = 800 * Math.cos( theta ) * Math.sin( phi );
94 | object.position.y = 800 * Math.sin( theta ) * Math.sin( phi );
95 | object.position.z = 800 * Math.cos( phi );
96 |
97 | vector.copy( object.position ).multiplyScalar( 2 );
98 |
99 | object.lookAt( vector );
100 |
101 | targets.sphere.push( object );
102 |
103 | }
104 |
105 | // helix
106 |
107 | var vector = new THREE.Vector3();
108 |
109 | for ( var i = 0, l = objects.length; i < l; i ++ ) {
110 |
111 | //var phi = i * 0.175 + Math.PI;
112 | var phi = i * 0.205 + Math.PI;
113 |
114 | var object = new THREE.Object3D();
115 |
116 | object.position.x = 900 * Math.sin( phi );
117 | object.position.y = - ( i * 8 ) + 450;
118 | object.position.z = 900 * Math.cos( phi );
119 |
120 | vector.x = object.position.x * 2;
121 | vector.y = object.position.y;
122 | vector.z = object.position.z * 2;
123 |
124 | object.lookAt( vector );
125 |
126 | targets.helix.push( object );
127 |
128 | }
129 |
130 | // grid
131 |
132 | for ( var i = 0; i < objects.length; i ++ ) {
133 |
134 | var object = new THREE.Object3D();
135 |
136 | object.position.x = ( ( i % 5 ) * 400 ) - 800;
137 | object.position.y = ( - ( Math.floor( i / 5 ) % 5 ) * 400 ) + 800;
138 | object.position.z = ( Math.floor( i / 25 ) ) * 1000 - 2000;
139 |
140 | targets.grid.push( object );
141 |
142 | }
143 |
144 | //
145 |
146 | renderer = new THREE.CSS3DRenderer();
147 | renderer.setSize( window.innerWidth, window.innerHeight );
148 | renderer.domElement.style.position = 'absolute';
149 | document.getElementById( 'container' ).appendChild( renderer.domElement );
150 |
151 | //
152 |
153 | controls = new THREE.TrackballControls( camera, renderer.domElement );
154 | controls.rotateSpeed = 0.5;
155 | controls.minDistance = 500;
156 | controls.maxDistance = 6000;
157 | controls.addEventListener( 'change', render );
158 |
159 | var button = document.getElementById( 'table' );
160 | button.addEventListener( 'click', function ( event ) {
161 |
162 | transform( targets.table, 2000 );
163 |
164 | }, false );
165 |
166 | var button = document.getElementById( 'sphere' );
167 | button.addEventListener( 'click', function ( event ) {
168 |
169 | transform( targets.sphere, 2000 );
170 |
171 | }, false );
172 |
173 | var button = document.getElementById( 'helix' );
174 | button.addEventListener( 'click', function ( event ) {
175 |
176 | transform( targets.helix, 2000 );
177 |
178 | }, false );
179 |
180 | var button = document.getElementById( 'grid' );
181 | button.addEventListener( 'click', function ( event ) {
182 |
183 | transform( targets.grid, 2000 );
184 |
185 | }, false );
186 |
187 | //transform( targets.helix, 5000 );
188 | transform( targets.table, 2000 );
189 | //
190 |
191 | window.addEventListener( 'resize', onWindowResize, false );
192 |
193 | }
194 |
195 | function transform( targets, duration ) {
196 |
197 | TWEEN.removeAll();
198 |
199 | for ( var i = 0; i < objects.length; i ++ ) {
200 |
201 | var object = objects[ i ];
202 | var target = targets[ i ];
203 |
204 | new TWEEN.Tween( object.position )
205 | .to( { x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration )
206 | .easing( TWEEN.Easing.Exponential.InOut )
207 | .start();
208 |
209 | new TWEEN.Tween( object.rotation )
210 | .to( { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration )
211 | .easing( TWEEN.Easing.Exponential.InOut )
212 | .start();
213 |
214 | }
215 | new TWEEN.Tween( this )
216 | .to( {}, duration * 2 )
217 | .onUpdate( render )
218 | .start();
219 | //*/
220 | }
221 |
222 | function onWindowResize() {
223 |
224 | camera.aspect = window.innerWidth / window.innerHeight;
225 | camera.updateProjectionMatrix();
226 |
227 | renderer.setSize( window.innerWidth, window.innerHeight );
228 |
229 | }
230 |
231 | function animate() {
232 |
233 | requestAnimationFrame( animate );
234 |
235 | TWEEN.update();
236 | controls.update();
237 | // render();
238 | //move( -1 );
239 |
240 | }
241 |
242 | function render() {
243 | /*
244 | theta += 0.1;
245 | */
246 |
247 | /*
248 | if (!isMove) {
249 | camera.position.x += Math.PI * 1;
250 | camera.position.y += 5;
251 | camera.position.z += 0.1;
252 | }
253 | camera.lookAt( scene.position );
254 | */
255 | renderer.render( scene, camera );
256 |
257 | }
258 |
259 | /**
260 | ** 控制自动旋转
261 | */
262 | /*
263 | var isMove = false;
264 |
265 | document.onmousedown = function(e) {
266 | isMove = true;
267 | };
268 |
269 | document.onmouseup = function() {
270 | isMove = false;
271 | animate();
272 | }
273 | */
274 |
275 | // 说明:用 JavaScript 实现网页图片等比例缩放
276 | function resizeImg(image,distWidth,distHeight)
277 | {
278 | srcWidth = image.width;
279 | srcHeight = image.height;
280 | var ratio = 1;
281 | if(srcWidth>0 && srcHeight>0)
282 | {
283 | if(srcWidth/srcHeight>= distWidth/distHeight)
284 | {
285 | if(srcWidth>distWidth)
286 | {
287 | ratio = distWidth/srcWidth;
288 | }
289 | }
290 | else
291 | {
292 | if(srcHeight>distHeight)
293 | {
294 | ratio = distHeight/srcHeight;
295 | }
296 | }
297 | }
298 | var width = srcWidth*ratio;
299 | var heigh = srcHeight*ratio;
300 |
301 | image.style.width = width.toString() + 'px';
302 | image.style.height = heigh.toString() + 'px';
303 |
304 | if(width < distWidth)
305 | image.style.paddingLeft = ((distWidth - width)/2).toString() + 'px';
306 |
307 | if(heigh < distHeight)
308 | image.style.paddingTop = ((distHeight - heigh)/2).toString() + 'px';
309 | }
310 |
311 | //获得指定文件夹图片名称列表,同时设置图片的位置
312 | function getImgs (uid){
313 |
314 | $.ajax({
315 | type: "GET",
316 | url: "/users/" + uid,
317 | //data: "",
318 | dataType: "json",
319 | success: function(data){
320 |
321 |
322 | // console.log(data);
323 | table = data;
324 | init();
325 | animate();
326 | },
327 | error: function(XMLHttpRequest, textStatus, errorThrown){
328 | console.error(XMLHttpRequest, textStatus, errorThrown);
329 | }
330 | });
331 | }
--------------------------------------------------------------------------------
/templates/wall2.phtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | pper
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
30 |
31 |
399 |
400 |
401 |
409 |
410 |
411 |
412 | We like to take photo anytime, anywhere, We love Click、Click!
413 |
414 |
415 |
416 |

417 |
418 |
Usage 使用方法:
419 |
420 | - 1、Scan the QR Code in WeChat
微信扫描二维码
421 | - 2、Send Photo 发送图片
422 | - 3、Refresh 刷新
423 |
424 |
425 |
426 |
427 |
428 |
429 |
--------------------------------------------------------------------------------
/public/wall2/tween.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author sole / http://soledadpenades.com
3 | * @author mrdoob / http://mrdoob.com
4 | * @author Robert Eisele / http://www.xarg.org
5 | * @author Philippe / http://philippe.elsass.me
6 | * @author Robert Penner / http://www.robertpenner.com/easing_terms_of_use.html
7 | * @author Paul Lewis / http://www.aerotwist.com/
8 | * @author lechecacharro
9 | * @author Josh Faul / http://jocafa.com/
10 | * @author egraether / http://egraether.com/
11 | * @author endel / http://endel.me
12 | * @author Ben Delarre / http://delarre.net
13 | */
14 |
15 | // Date.now shim for (ahem) Internet Explo(d|r)er
16 | if ( Date.now === undefined ) {
17 |
18 | Date.now = function () {
19 |
20 | return new Date().valueOf();
21 |
22 | };
23 |
24 | }
25 |
26 | var TWEEN = TWEEN || ( function () {
27 |
28 | var _tweens = [];
29 |
30 | return {
31 |
32 | REVISION: '12',
33 |
34 | getAll: function () {
35 |
36 | return _tweens;
37 |
38 | },
39 |
40 | removeAll: function () {
41 |
42 | _tweens = [];
43 |
44 | },
45 |
46 | add: function ( tween ) {
47 |
48 | _tweens.push( tween );
49 |
50 | },
51 |
52 | remove: function ( tween ) {
53 |
54 | var i = _tweens.indexOf( tween );
55 |
56 | if ( i !== -1 ) {
57 |
58 | _tweens.splice( i, 1 );
59 |
60 | }
61 |
62 | },
63 |
64 | update: function ( time ) {
65 |
66 | if ( _tweens.length === 0 ) return false;
67 |
68 | var i = 0;
69 |
70 | time = time !== undefined ? time : ( typeof window !== 'undefined' && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now() );
71 |
72 | while ( i < _tweens.length ) {
73 |
74 | if ( _tweens[ i ].update( time ) ) {
75 |
76 | i++;
77 |
78 | } else {
79 |
80 | _tweens.splice( i, 1 );
81 |
82 | }
83 |
84 | }
85 |
86 | return true;
87 |
88 | }
89 | };
90 |
91 | } )();
92 |
93 | TWEEN.Tween = function ( object ) {
94 |
95 | var _object = object;
96 | var _valuesStart = {};
97 | var _valuesEnd = {};
98 | var _valuesStartRepeat = {};
99 | var _duration = 1000;
100 | var _repeat = 0;
101 | var _yoyo = false;
102 | var _isPlaying = false;
103 | var _reversed = false;
104 | var _delayTime = 0;
105 | var _startTime = null;
106 | var _easingFunction = TWEEN.Easing.Linear.None;
107 | var _interpolationFunction = TWEEN.Interpolation.Linear;
108 | var _chainedTweens = [];
109 | var _onStartCallback = null;
110 | var _onStartCallbackFired = false;
111 | var _onUpdateCallback = null;
112 | var _onCompleteCallback = null;
113 |
114 | // Set all starting values present on the target object
115 | for ( var field in object ) {
116 |
117 | _valuesStart[ field ] = parseFloat(object[field], 10);
118 |
119 | }
120 |
121 | this.to = function ( properties, duration ) {
122 |
123 | if ( duration !== undefined ) {
124 |
125 | _duration = duration;
126 |
127 | }
128 |
129 | _valuesEnd = properties;
130 |
131 | return this;
132 |
133 | };
134 |
135 | this.start = function ( time ) {
136 |
137 | TWEEN.add( this );
138 |
139 | _isPlaying = true;
140 |
141 | _onStartCallbackFired = false;
142 |
143 | _startTime = time !== undefined ? time : ( typeof window !== 'undefined' && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now() );
144 | _startTime += _delayTime;
145 |
146 | for ( var property in _valuesEnd ) {
147 |
148 | // check if an Array was provided as property value
149 | if ( _valuesEnd[ property ] instanceof Array ) {
150 |
151 | if ( _valuesEnd[ property ].length === 0 ) {
152 |
153 | continue;
154 |
155 | }
156 |
157 | // create a local copy of the Array with the start value at the front
158 | _valuesEnd[ property ] = [ _object[ property ] ].concat( _valuesEnd[ property ] );
159 |
160 | }
161 |
162 | _valuesStart[ property ] = _object[ property ];
163 |
164 | if( ( _valuesStart[ property ] instanceof Array ) === false ) {
165 | _valuesStart[ property ] *= 1.0; // Ensures we're using numbers, not strings
166 | }
167 |
168 | _valuesStartRepeat[ property ] = _valuesStart[ property ] || 0;
169 |
170 | }
171 |
172 | return this;
173 |
174 | };
175 |
176 | this.stop = function () {
177 |
178 | if ( !_isPlaying ) {
179 | return this;
180 | }
181 |
182 | TWEEN.remove( this );
183 | _isPlaying = false;
184 | this.stopChainedTweens();
185 | return this;
186 |
187 | };
188 |
189 | this.stopChainedTweens = function () {
190 |
191 | for ( var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++ ) {
192 |
193 | _chainedTweens[ i ].stop();
194 |
195 | }
196 |
197 | };
198 |
199 | this.delay = function ( amount ) {
200 |
201 | _delayTime = amount;
202 | return this;
203 |
204 | };
205 |
206 | this.repeat = function ( times ) {
207 |
208 | _repeat = times;
209 | return this;
210 |
211 | };
212 |
213 | this.yoyo = function( yoyo ) {
214 |
215 | _yoyo = yoyo;
216 | return this;
217 |
218 | };
219 |
220 |
221 | this.easing = function ( easing ) {
222 |
223 | _easingFunction = easing;
224 | return this;
225 |
226 | };
227 |
228 | this.interpolation = function ( interpolation ) {
229 |
230 | _interpolationFunction = interpolation;
231 | return this;
232 |
233 | };
234 |
235 | this.chain = function () {
236 |
237 | _chainedTweens = arguments;
238 | return this;
239 |
240 | };
241 |
242 | this.onStart = function ( callback ) {
243 |
244 | _onStartCallback = callback;
245 | return this;
246 |
247 | };
248 |
249 | this.onUpdate = function ( callback ) {
250 |
251 | _onUpdateCallback = callback;
252 | return this;
253 |
254 | };
255 |
256 | this.onComplete = function ( callback ) {
257 |
258 | _onCompleteCallback = callback;
259 | return this;
260 |
261 | };
262 |
263 | this.update = function ( time ) {
264 |
265 | var property;
266 |
267 | if ( time < _startTime ) {
268 |
269 | return true;
270 |
271 | }
272 |
273 | if ( _onStartCallbackFired === false ) {
274 |
275 | if ( _onStartCallback !== null ) {
276 |
277 | _onStartCallback.call( _object );
278 |
279 | }
280 |
281 | _onStartCallbackFired = true;
282 |
283 | }
284 |
285 | var elapsed = ( time - _startTime ) / _duration;
286 | elapsed = elapsed > 1 ? 1 : elapsed;
287 |
288 | var value = _easingFunction( elapsed );
289 |
290 | for ( property in _valuesEnd ) {
291 |
292 | var start = _valuesStart[ property ] || 0;
293 | var end = _valuesEnd[ property ];
294 |
295 | if ( end instanceof Array ) {
296 |
297 | _object[ property ] = _interpolationFunction( end, value );
298 |
299 | } else {
300 |
301 | // Parses relative end values with start as base (e.g.: +10, -3)
302 | if ( typeof(end) === "string" ) {
303 | end = start + parseFloat(end, 10);
304 | }
305 |
306 | // protect against non numeric properties.
307 | if ( typeof(end) === "number" ) {
308 | _object[ property ] = start + ( end - start ) * value;
309 | }
310 |
311 | }
312 |
313 | }
314 |
315 | if ( _onUpdateCallback !== null ) {
316 |
317 | _onUpdateCallback.call( _object, value );
318 |
319 | }
320 |
321 | if ( elapsed == 1 ) {
322 |
323 | if ( _repeat > 0 ) {
324 |
325 | if( isFinite( _repeat ) ) {
326 | _repeat--;
327 | }
328 |
329 | // reassign starting values, restart by making startTime = now
330 | for( property in _valuesStartRepeat ) {
331 |
332 | if ( typeof( _valuesEnd[ property ] ) === "string" ) {
333 | _valuesStartRepeat[ property ] = _valuesStartRepeat[ property ] + parseFloat(_valuesEnd[ property ], 10);
334 | }
335 |
336 | if (_yoyo) {
337 | var tmp = _valuesStartRepeat[ property ];
338 | _valuesStartRepeat[ property ] = _valuesEnd[ property ];
339 | _valuesEnd[ property ] = tmp;
340 | _reversed = !_reversed;
341 | }
342 | _valuesStart[ property ] = _valuesStartRepeat[ property ];
343 |
344 | }
345 |
346 | _startTime = time + _delayTime;
347 |
348 | return true;
349 |
350 | } else {
351 |
352 | if ( _onCompleteCallback !== null ) {
353 |
354 | _onCompleteCallback.call( _object );
355 |
356 | }
357 |
358 | for ( var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++ ) {
359 |
360 | _chainedTweens[ i ].start( time );
361 |
362 | }
363 |
364 | return false;
365 |
366 | }
367 |
368 | }
369 |
370 | return true;
371 |
372 | };
373 |
374 | };
375 |
376 |
377 | TWEEN.Easing = {
378 |
379 | Linear: {
380 |
381 | None: function ( k ) {
382 |
383 | return k;
384 |
385 | }
386 |
387 | },
388 |
389 | Quadratic: {
390 |
391 | In: function ( k ) {
392 |
393 | return k * k;
394 |
395 | },
396 |
397 | Out: function ( k ) {
398 |
399 | return k * ( 2 - k );
400 |
401 | },
402 |
403 | InOut: function ( k ) {
404 |
405 | if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
406 | return - 0.5 * ( --k * ( k - 2 ) - 1 );
407 |
408 | }
409 |
410 | },
411 |
412 | Cubic: {
413 |
414 | In: function ( k ) {
415 |
416 | return k * k * k;
417 |
418 | },
419 |
420 | Out: function ( k ) {
421 |
422 | return --k * k * k + 1;
423 |
424 | },
425 |
426 | InOut: function ( k ) {
427 |
428 | if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k;
429 | return 0.5 * ( ( k -= 2 ) * k * k + 2 );
430 |
431 | }
432 |
433 | },
434 |
435 | Quartic: {
436 |
437 | In: function ( k ) {
438 |
439 | return k * k * k * k;
440 |
441 | },
442 |
443 | Out: function ( k ) {
444 |
445 | return 1 - ( --k * k * k * k );
446 |
447 | },
448 |
449 | InOut: function ( k ) {
450 |
451 | if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k;
452 | return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 );
453 |
454 | }
455 |
456 | },
457 |
458 | Quintic: {
459 |
460 | In: function ( k ) {
461 |
462 | return k * k * k * k * k;
463 |
464 | },
465 |
466 | Out: function ( k ) {
467 |
468 | return --k * k * k * k * k + 1;
469 |
470 | },
471 |
472 | InOut: function ( k ) {
473 |
474 | if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k;
475 | return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 );
476 |
477 | }
478 |
479 | },
480 |
481 | Sinusoidal: {
482 |
483 | In: function ( k ) {
484 |
485 | return 1 - Math.cos( k * Math.PI / 2 );
486 |
487 | },
488 |
489 | Out: function ( k ) {
490 |
491 | return Math.sin( k * Math.PI / 2 );
492 |
493 | },
494 |
495 | InOut: function ( k ) {
496 |
497 | return 0.5 * ( 1 - Math.cos( Math.PI * k ) );
498 |
499 | }
500 |
501 | },
502 |
503 | Exponential: {
504 |
505 | In: function ( k ) {
506 |
507 | return k === 0 ? 0 : Math.pow( 1024, k - 1 );
508 |
509 | },
510 |
511 | Out: function ( k ) {
512 |
513 | return k === 1 ? 1 : 1 - Math.pow( 2, - 10 * k );
514 |
515 | },
516 |
517 | InOut: function ( k ) {
518 |
519 | if ( k === 0 ) return 0;
520 | if ( k === 1 ) return 1;
521 | if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 1024, k - 1 );
522 | return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 );
523 |
524 | }
525 |
526 | },
527 |
528 | Circular: {
529 |
530 | In: function ( k ) {
531 |
532 | return 1 - Math.sqrt( 1 - k * k );
533 |
534 | },
535 |
536 | Out: function ( k ) {
537 |
538 | return Math.sqrt( 1 - ( --k * k ) );
539 |
540 | },
541 |
542 | InOut: function ( k ) {
543 |
544 | if ( ( k *= 2 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1);
545 | return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1);
546 |
547 | }
548 |
549 | },
550 |
551 | Elastic: {
552 |
553 | In: function ( k ) {
554 |
555 | var s, a = 0.1, p = 0.4;
556 | if ( k === 0 ) return 0;
557 | if ( k === 1 ) return 1;
558 | if ( !a || a < 1 ) { a = 1; s = p / 4; }
559 | else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
560 | return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
561 |
562 | },
563 |
564 | Out: function ( k ) {
565 |
566 | var s, a = 0.1, p = 0.4;
567 | if ( k === 0 ) return 0;
568 | if ( k === 1 ) return 1;
569 | if ( !a || a < 1 ) { a = 1; s = p / 4; }
570 | else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
571 | return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
572 |
573 | },
574 |
575 | InOut: function ( k ) {
576 |
577 | var s, a = 0.1, p = 0.4;
578 | if ( k === 0 ) return 0;
579 | if ( k === 1 ) return 1;
580 | if ( !a || a < 1 ) { a = 1; s = p / 4; }
581 | else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
582 | if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
583 | return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1;
584 |
585 | }
586 |
587 | },
588 |
589 | Back: {
590 |
591 | In: function ( k ) {
592 |
593 | var s = 1.70158;
594 | return k * k * ( ( s + 1 ) * k - s );
595 |
596 | },
597 |
598 | Out: function ( k ) {
599 |
600 | var s = 1.70158;
601 | return --k * k * ( ( s + 1 ) * k + s ) + 1;
602 |
603 | },
604 |
605 | InOut: function ( k ) {
606 |
607 | var s = 1.70158 * 1.525;
608 | if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) );
609 | return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 );
610 |
611 | }
612 |
613 | },
614 |
615 | Bounce: {
616 |
617 | In: function ( k ) {
618 |
619 | return 1 - TWEEN.Easing.Bounce.Out( 1 - k );
620 |
621 | },
622 |
623 | Out: function ( k ) {
624 |
625 | if ( k < ( 1 / 2.75 ) ) {
626 |
627 | return 7.5625 * k * k;
628 |
629 | } else if ( k < ( 2 / 2.75 ) ) {
630 |
631 | return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
632 |
633 | } else if ( k < ( 2.5 / 2.75 ) ) {
634 |
635 | return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
636 |
637 | } else {
638 |
639 | return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
640 |
641 | }
642 |
643 | },
644 |
645 | InOut: function ( k ) {
646 |
647 | if ( k < 0.5 ) return TWEEN.Easing.Bounce.In( k * 2 ) * 0.5;
648 | return TWEEN.Easing.Bounce.Out( k * 2 - 1 ) * 0.5 + 0.5;
649 |
650 | }
651 |
652 | }
653 |
654 | };
655 |
656 | TWEEN.Interpolation = {
657 |
658 | Linear: function ( v, k ) {
659 |
660 | var m = v.length - 1, f = m * k, i = Math.floor( f ), fn = TWEEN.Interpolation.Utils.Linear;
661 |
662 | if ( k < 0 ) return fn( v[ 0 ], v[ 1 ], f );
663 | if ( k > 1 ) return fn( v[ m ], v[ m - 1 ], m - f );
664 |
665 | return fn( v[ i ], v[ i + 1 > m ? m : i + 1 ], f - i );
666 |
667 | },
668 |
669 | Bezier: function ( v, k ) {
670 |
671 | var b = 0, n = v.length - 1, pw = Math.pow, bn = TWEEN.Interpolation.Utils.Bernstein, i;
672 |
673 | for ( i = 0; i <= n; i++ ) {
674 | b += pw( 1 - k, n - i ) * pw( k, i ) * v[ i ] * bn( n, i );
675 | }
676 |
677 | return b;
678 |
679 | },
680 |
681 | CatmullRom: function ( v, k ) {
682 |
683 | var m = v.length - 1, f = m * k, i = Math.floor( f ), fn = TWEEN.Interpolation.Utils.CatmullRom;
684 |
685 | if ( v[ 0 ] === v[ m ] ) {
686 |
687 | if ( k < 0 ) i = Math.floor( f = m * ( 1 + k ) );
688 |
689 | return fn( v[ ( i - 1 + m ) % m ], v[ i ], v[ ( i + 1 ) % m ], v[ ( i + 2 ) % m ], f - i );
690 |
691 | } else {
692 |
693 | if ( k < 0 ) return v[ 0 ] - ( fn( v[ 0 ], v[ 0 ], v[ 1 ], v[ 1 ], -f ) - v[ 0 ] );
694 | if ( k > 1 ) return v[ m ] - ( fn( v[ m ], v[ m ], v[ m - 1 ], v[ m - 1 ], f - m ) - v[ m ] );
695 |
696 | return fn( v[ i ? i - 1 : 0 ], v[ i ], v[ m < i + 1 ? m : i + 1 ], v[ m < i + 2 ? m : i + 2 ], f - i );
697 |
698 | }
699 |
700 | },
701 |
702 | Utils: {
703 |
704 | Linear: function ( p0, p1, t ) {
705 |
706 | return ( p1 - p0 ) * t + p0;
707 |
708 | },
709 |
710 | Bernstein: function ( n , i ) {
711 |
712 | var fc = TWEEN.Interpolation.Utils.Factorial;
713 | return fc( n ) / fc( i ) / fc( n - i );
714 |
715 | },
716 |
717 | Factorial: ( function () {
718 |
719 | var a = [ 1 ];
720 |
721 | return function ( n ) {
722 |
723 | var s = 1, i;
724 | if ( a[ n ] ) return a[ n ];
725 | for ( i = n; i > 1; i-- ) s *= i;
726 | return a[ n ] = s;
727 |
728 | };
729 |
730 | } )(),
731 |
732 | CatmullRom: function ( p0, p1, p2, p3, t ) {
733 |
734 | var v0 = ( p2 - p0 ) * 0.5, v1 = ( p3 - p1 ) * 0.5, t2 = t * t, t3 = t * t2;
735 | return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
736 |
737 | }
738 |
739 | }
740 |
741 | };
742 |
--------------------------------------------------------------------------------
/public/js/TrackballControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Eberhard Graether / http://egraether.com/
3 | * @author Mark Lundin / http://mark-lundin.com
4 | * @author Simone Manini / http://daron1337.github.io
5 | * @author Luca Antiga / http://lantiga.github.io
6 | */
7 |
8 | THREE.TrackballControls = function ( object, domElement ) {
9 |
10 | var _this = this;
11 | var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
12 |
13 | this.object = object;
14 | this.domElement = ( domElement !== undefined ) ? domElement : document;
15 |
16 | // API
17 |
18 | this.enabled = true;
19 |
20 | this.screen = { left: 0, top: 0, width: 0, height: 0 };
21 |
22 | this.rotateSpeed = 1.0;
23 | this.zoomSpeed = 1.2;
24 | this.panSpeed = 0.3;
25 |
26 | this.noRotate = false;
27 | this.noZoom = false;
28 | this.noPan = false;
29 |
30 | this.staticMoving = false;
31 | this.dynamicDampingFactor = 0.2;
32 |
33 | this.minDistance = 0;
34 | this.maxDistance = Infinity;
35 |
36 | this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
37 |
38 | // internals
39 |
40 | this.target = new THREE.Vector3();
41 |
42 | var EPS = 0.000001;
43 |
44 | var lastPosition = new THREE.Vector3();
45 |
46 | var _state = STATE.NONE,
47 | _prevState = STATE.NONE,
48 |
49 | _eye = new THREE.Vector3(),
50 |
51 | _movePrev = new THREE.Vector2(),
52 | _moveCurr = new THREE.Vector2(),
53 |
54 | _lastAxis = new THREE.Vector3(),
55 | _lastAngle = 0,
56 |
57 | _zoomStart = new THREE.Vector2(),
58 | _zoomEnd = new THREE.Vector2(),
59 |
60 | _touchZoomDistanceStart = 0,
61 | _touchZoomDistanceEnd = 0,
62 |
63 | _panStart = new THREE.Vector2(),
64 | _panEnd = new THREE.Vector2();
65 |
66 | // for reset
67 |
68 | this.target0 = this.target.clone();
69 | this.position0 = this.object.position.clone();
70 | this.up0 = this.object.up.clone();
71 |
72 | // events
73 |
74 | var changeEvent = { type: 'change' };
75 | var startEvent = { type: 'start' };
76 | var endEvent = { type: 'end' };
77 |
78 |
79 | // methods
80 |
81 | this.handleResize = function () {
82 |
83 | if ( this.domElement === document ) {
84 |
85 | this.screen.left = 0;
86 | this.screen.top = 0;
87 | this.screen.width = window.innerWidth;
88 | this.screen.height = window.innerHeight;
89 |
90 | } else {
91 |
92 | var box = this.domElement.getBoundingClientRect();
93 | // adjustments come from similar code in the jquery offset() function
94 | var d = this.domElement.ownerDocument.documentElement;
95 | this.screen.left = box.left + window.pageXOffset - d.clientLeft;
96 | this.screen.top = box.top + window.pageYOffset - d.clientTop;
97 | this.screen.width = box.width;
98 | this.screen.height = box.height;
99 |
100 | }
101 |
102 | };
103 |
104 | var getMouseOnScreen = ( function () {
105 |
106 | var vector = new THREE.Vector2();
107 |
108 | return function getMouseOnScreen( pageX, pageY ) {
109 |
110 | vector.set(
111 | ( pageX - _this.screen.left ) / _this.screen.width,
112 | ( pageY - _this.screen.top ) / _this.screen.height
113 | );
114 |
115 | return vector;
116 |
117 | };
118 |
119 | }() );
120 |
121 | var getMouseOnCircle = ( function () {
122 |
123 | var vector = new THREE.Vector2();
124 |
125 | return function getMouseOnCircle( pageX, pageY ) {
126 |
127 | vector.set(
128 | ( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / ( _this.screen.width * 0.5 ) ),
129 | ( ( _this.screen.height + 2 * ( _this.screen.top - pageY ) ) / _this.screen.width ) // screen.width intentional
130 | );
131 |
132 | return vector;
133 |
134 | };
135 |
136 | }() );
137 |
138 | this.rotateCamera = ( function () {
139 |
140 | var axis = new THREE.Vector3(),
141 | quaternion = new THREE.Quaternion(),
142 | eyeDirection = new THREE.Vector3(),
143 | objectUpDirection = new THREE.Vector3(),
144 | objectSidewaysDirection = new THREE.Vector3(),
145 | moveDirection = new THREE.Vector3(),
146 | angle;
147 |
148 | return function rotateCamera() {
149 |
150 | moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
151 | angle = moveDirection.length();
152 |
153 | if ( angle ) {
154 |
155 | _eye.copy( _this.object.position ).sub( _this.target );
156 |
157 | eyeDirection.copy( _eye ).normalize();
158 | objectUpDirection.copy( _this.object.up ).normalize();
159 | objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
160 |
161 | objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
162 | objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
163 |
164 | moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
165 |
166 | axis.crossVectors( moveDirection, _eye ).normalize();
167 |
168 | angle *= _this.rotateSpeed;
169 | quaternion.setFromAxisAngle( axis, angle );
170 |
171 | _eye.applyQuaternion( quaternion );
172 | _this.object.up.applyQuaternion( quaternion );
173 |
174 | _lastAxis.copy( axis );
175 | _lastAngle = angle;
176 |
177 | } else if ( ! _this.staticMoving && _lastAngle ) {
178 |
179 | _lastAngle *= Math.sqrt( 1.0 - _this.dynamicDampingFactor );
180 | _eye.copy( _this.object.position ).sub( _this.target );
181 | quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
182 | _eye.applyQuaternion( quaternion );
183 | _this.object.up.applyQuaternion( quaternion );
184 |
185 | }
186 |
187 | _movePrev.copy( _moveCurr );
188 |
189 | };
190 |
191 | }() );
192 |
193 |
194 | this.zoomCamera = function () {
195 |
196 | var factor;
197 |
198 | if ( _state === STATE.TOUCH_ZOOM_PAN ) {
199 |
200 | factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
201 | _touchZoomDistanceStart = _touchZoomDistanceEnd;
202 | _eye.multiplyScalar( factor );
203 |
204 | } else {
205 |
206 | factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
207 |
208 | if ( factor !== 1.0 && factor > 0.0 ) {
209 |
210 | _eye.multiplyScalar( factor );
211 |
212 | }
213 |
214 | if ( _this.staticMoving ) {
215 |
216 | _zoomStart.copy( _zoomEnd );
217 |
218 | } else {
219 |
220 | _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
221 |
222 | }
223 |
224 | }
225 |
226 | };
227 |
228 | this.panCamera = ( function () {
229 |
230 | var mouseChange = new THREE.Vector2(),
231 | objectUp = new THREE.Vector3(),
232 | pan = new THREE.Vector3();
233 |
234 | return function panCamera() {
235 |
236 | mouseChange.copy( _panEnd ).sub( _panStart );
237 |
238 | if ( mouseChange.lengthSq() ) {
239 |
240 | mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
241 |
242 | pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
243 | pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
244 |
245 | _this.object.position.add( pan );
246 | _this.target.add( pan );
247 |
248 | if ( _this.staticMoving ) {
249 |
250 | _panStart.copy( _panEnd );
251 |
252 | } else {
253 |
254 | _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
255 |
256 | }
257 |
258 | }
259 |
260 | };
261 |
262 | }() );
263 |
264 | this.checkDistances = function () {
265 |
266 | if ( ! _this.noZoom || ! _this.noPan ) {
267 |
268 | if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
269 |
270 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
271 | _zoomStart.copy( _zoomEnd );
272 |
273 | }
274 |
275 | if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
276 |
277 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
278 | _zoomStart.copy( _zoomEnd );
279 |
280 | }
281 |
282 | }
283 |
284 | };
285 |
286 | this.update = function () {
287 |
288 | _eye.subVectors( _this.object.position, _this.target );
289 |
290 | if ( ! _this.noRotate ) {
291 |
292 | _this.rotateCamera();
293 |
294 | }
295 |
296 | if ( ! _this.noZoom ) {
297 |
298 | _this.zoomCamera();
299 |
300 | }
301 |
302 | if ( ! _this.noPan ) {
303 |
304 | _this.panCamera();
305 |
306 | }
307 |
308 | _this.object.position.addVectors( _this.target, _eye );
309 |
310 | _this.checkDistances();
311 |
312 | _this.object.lookAt( _this.target );
313 |
314 | if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) {
315 |
316 | _this.dispatchEvent( changeEvent );
317 |
318 | lastPosition.copy( _this.object.position );
319 |
320 | }
321 |
322 | };
323 |
324 | this.reset = function () {
325 |
326 | _state = STATE.NONE;
327 | _prevState = STATE.NONE;
328 |
329 | _this.target.copy( _this.target0 );
330 | _this.object.position.copy( _this.position0 );
331 | _this.object.up.copy( _this.up0 );
332 |
333 | _eye.subVectors( _this.object.position, _this.target );
334 |
335 | _this.object.lookAt( _this.target );
336 |
337 | _this.dispatchEvent( changeEvent );
338 |
339 | lastPosition.copy( _this.object.position );
340 |
341 | };
342 |
343 | // listeners
344 |
345 | function keydown( event ) {
346 |
347 | if ( _this.enabled === false ) return;
348 |
349 | window.removeEventListener( 'keydown', keydown );
350 |
351 | _prevState = _state;
352 |
353 | if ( _state !== STATE.NONE ) {
354 |
355 | return;
356 |
357 | } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ) {
358 |
359 | _state = STATE.ROTATE;
360 |
361 | } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && ! _this.noZoom ) {
362 |
363 | _state = STATE.ZOOM;
364 |
365 | } else if ( event.keyCode === _this.keys[ STATE.PAN ] && ! _this.noPan ) {
366 |
367 | _state = STATE.PAN;
368 |
369 | }
370 |
371 | }
372 |
373 | function keyup( event ) {
374 |
375 | if ( _this.enabled === false ) return;
376 |
377 | _state = _prevState;
378 |
379 | window.addEventListener( 'keydown', keydown, false );
380 |
381 | }
382 |
383 | function mousedown( event ) {
384 |
385 | if ( _this.enabled === false ) return;
386 |
387 | event.preventDefault();
388 | event.stopPropagation();
389 |
390 | if ( _state === STATE.NONE ) {
391 |
392 | _state = event.button;
393 |
394 | }
395 |
396 | if ( _state === STATE.ROTATE && ! _this.noRotate ) {
397 |
398 | _moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
399 | _movePrev.copy( _moveCurr );
400 |
401 | } else if ( _state === STATE.ZOOM && ! _this.noZoom ) {
402 |
403 | _zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
404 | _zoomEnd.copy( _zoomStart );
405 |
406 | } else if ( _state === STATE.PAN && ! _this.noPan ) {
407 |
408 | _panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
409 | _panEnd.copy( _panStart );
410 |
411 | }
412 |
413 | document.addEventListener( 'mousemove', mousemove, false );
414 | document.addEventListener( 'mouseup', mouseup, false );
415 |
416 | _this.dispatchEvent( startEvent );
417 |
418 | }
419 |
420 | function mousemove( event ) {
421 |
422 | if ( _this.enabled === false ) return;
423 |
424 | event.preventDefault();
425 | event.stopPropagation();
426 |
427 | if ( _state === STATE.ROTATE && ! _this.noRotate ) {
428 |
429 | _movePrev.copy( _moveCurr );
430 | _moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
431 |
432 | } else if ( _state === STATE.ZOOM && ! _this.noZoom ) {
433 |
434 | _zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
435 |
436 | } else if ( _state === STATE.PAN && ! _this.noPan ) {
437 |
438 | _panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
439 |
440 | }
441 |
442 | }
443 |
444 | function mouseup( event ) {
445 |
446 | if ( _this.enabled === false ) return;
447 |
448 | event.preventDefault();
449 | event.stopPropagation();
450 |
451 | _state = STATE.NONE;
452 |
453 | document.removeEventListener( 'mousemove', mousemove );
454 | document.removeEventListener( 'mouseup', mouseup );
455 | _this.dispatchEvent( endEvent );
456 |
457 | }
458 |
459 | function mousewheel( event ) {
460 |
461 | if ( _this.enabled === false ) return;
462 |
463 | if ( _this.noZoom === true ) return;
464 |
465 | event.preventDefault();
466 | event.stopPropagation();
467 |
468 | switch ( event.deltaMode ) {
469 |
470 | case 2:
471 | // Zoom in pages
472 | _zoomStart.y -= event.deltaY * 0.025;
473 | break;
474 |
475 | case 1:
476 | // Zoom in lines
477 | _zoomStart.y -= event.deltaY * 0.01;
478 | break;
479 |
480 | default:
481 | // undefined, 0, assume pixels
482 | _zoomStart.y -= event.deltaY * 0.00025;
483 | break;
484 |
485 | }
486 |
487 | _this.dispatchEvent( startEvent );
488 | _this.dispatchEvent( endEvent );
489 |
490 | }
491 |
492 | function touchstart( event ) {
493 |
494 | if ( _this.enabled === false ) return;
495 |
496 | event.preventDefault();
497 |
498 | switch ( event.touches.length ) {
499 |
500 | case 1:
501 | _state = STATE.TOUCH_ROTATE;
502 | _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
503 | _movePrev.copy( _moveCurr );
504 | break;
505 |
506 | default: // 2 or more
507 | _state = STATE.TOUCH_ZOOM_PAN;
508 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
509 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
510 | _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
511 |
512 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
513 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
514 | _panStart.copy( getMouseOnScreen( x, y ) );
515 | _panEnd.copy( _panStart );
516 | break;
517 |
518 | }
519 |
520 | _this.dispatchEvent( startEvent );
521 |
522 | }
523 |
524 | function touchmove( event ) {
525 |
526 | if ( _this.enabled === false ) return;
527 |
528 | event.preventDefault();
529 | event.stopPropagation();
530 |
531 | switch ( event.touches.length ) {
532 |
533 | case 1:
534 | _movePrev.copy( _moveCurr );
535 | _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
536 | break;
537 |
538 | default: // 2 or more
539 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
540 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
541 | _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
542 |
543 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
544 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
545 | _panEnd.copy( getMouseOnScreen( x, y ) );
546 | break;
547 |
548 | }
549 |
550 | }
551 |
552 | function touchend( event ) {
553 |
554 | if ( _this.enabled === false ) return;
555 |
556 | switch ( event.touches.length ) {
557 |
558 | case 0:
559 | _state = STATE.NONE;
560 | break;
561 |
562 | case 1:
563 | _state = STATE.TOUCH_ROTATE;
564 | _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
565 | _movePrev.copy( _moveCurr );
566 | break;
567 |
568 | }
569 |
570 | _this.dispatchEvent( endEvent );
571 |
572 | }
573 |
574 | function contextmenu( event ) {
575 |
576 | if ( _this.enabled === false ) return;
577 |
578 | event.preventDefault();
579 |
580 | }
581 |
582 | this.dispose = function () {
583 |
584 | this.domElement.removeEventListener( 'contextmenu', contextmenu, false );
585 | this.domElement.removeEventListener( 'mousedown', mousedown, false );
586 | this.domElement.removeEventListener( 'wheel', mousewheel, false );
587 |
588 | this.domElement.removeEventListener( 'touchstart', touchstart, false );
589 | this.domElement.removeEventListener( 'touchend', touchend, false );
590 | this.domElement.removeEventListener( 'touchmove', touchmove, false );
591 |
592 | document.removeEventListener( 'mousemove', mousemove, false );
593 | document.removeEventListener( 'mouseup', mouseup, false );
594 |
595 | window.removeEventListener( 'keydown', keydown, false );
596 | window.removeEventListener( 'keyup', keyup, false );
597 |
598 | };
599 |
600 | this.domElement.addEventListener( 'contextmenu', contextmenu, false );
601 | this.domElement.addEventListener( 'mousedown', mousedown, false );
602 | this.domElement.addEventListener( 'wheel', mousewheel, false );
603 |
604 | this.domElement.addEventListener( 'touchstart', touchstart, false );
605 | this.domElement.addEventListener( 'touchend', touchend, false );
606 | this.domElement.addEventListener( 'touchmove', touchmove, false );
607 |
608 | window.addEventListener( 'keydown', keydown, false );
609 | window.addEventListener( 'keyup', keyup, false );
610 |
611 | this.handleResize();
612 |
613 | // force an update at start
614 | this.update();
615 |
616 | };
617 |
618 | THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
619 | THREE.TrackballControls.prototype.constructor = THREE.TrackballControls;
620 |
--------------------------------------------------------------------------------
/public/wall2/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 | /*global THREE, console */
9 |
10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains
11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
12 | // supported.
13 | //
14 | // Orbit - left mouse / touch: one finger move
15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
16 | // Pan - right mouse, or arrow keys / touch: three finter swipe
17 | //
18 | // This is a drop-in replacement for (most) TrackballControls used in examples.
19 | // That is, include this js file and wherever you see:
20 | // controls = new THREE.TrackballControls( camera );
21 | // controls.target.z = 150;
22 | // Simple substitute "OrbitControls" and the control should work as-is.
23 |
24 | THREE.OrbitControls = function ( object, domElement ) {
25 |
26 | this.object = object;
27 | this.domElement = ( domElement !== undefined ) ? domElement : document;
28 |
29 | // API
30 |
31 | // Set to false to disable this control
32 | this.enabled = true;
33 |
34 | // "target" sets the location of focus, where the control orbits around
35 | // and where it pans with respect to.
36 | this.target = new THREE.Vector3();
37 | // center is old, deprecated; use "target" instead
38 | this.center = this.target;
39 |
40 | // This option actually enables dollying in and out; left as "zoom" for
41 | // backwards compatibility
42 | this.noZoom = false;
43 | this.zoomSpeed = 1.0;
44 | // Limits to how far you can dolly in and out
45 | this.minDistance = 0;
46 | this.maxDistance = Infinity;
47 |
48 | // Set to true to disable this control
49 | this.noRotate = false;
50 | this.rotateSpeed = 1.0;
51 |
52 | // Set to true to disable this control
53 | this.noPan = false;
54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
55 |
56 | // Set to true to automatically rotate around the target
57 | this.autoRotate = false;
58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
59 |
60 | // How far you can orbit vertically, upper and lower limits.
61 | // Range is 0 to Math.PI radians.
62 | this.minPolarAngle = 0; // radians
63 | this.maxPolarAngle = Math.PI; // radians
64 |
65 | // Set to true to disable use of the keys
66 | this.noKeys = false;
67 | // The four arrow keys
68 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
69 |
70 | ////////////
71 | // internals
72 |
73 | var scope = this;
74 |
75 | var EPS = 0.000001;
76 |
77 | var rotateStart = new THREE.Vector2();
78 | var rotateEnd = new THREE.Vector2();
79 | var rotateDelta = new THREE.Vector2();
80 |
81 | var panStart = new THREE.Vector2();
82 | var panEnd = new THREE.Vector2();
83 | var panDelta = new THREE.Vector2();
84 |
85 | var dollyStart = new THREE.Vector2();
86 | var dollyEnd = new THREE.Vector2();
87 | var dollyDelta = new THREE.Vector2();
88 |
89 | var phiDelta = 0;
90 | var thetaDelta = 0;
91 | var scale = 1;
92 | var pan = new THREE.Vector3();
93 |
94 | var lastPosition = new THREE.Vector3();
95 |
96 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
97 | var state = STATE.NONE;
98 |
99 | // events
100 |
101 | var changeEvent = { type: 'change' };
102 |
103 |
104 | this.rotateLeft = function ( angle ) {
105 |
106 | if ( angle === undefined ) {
107 |
108 | angle = getAutoRotationAngle();
109 |
110 | }
111 |
112 | thetaDelta -= angle;
113 |
114 | };
115 |
116 | this.rotateUp = function ( angle ) {
117 |
118 | if ( angle === undefined ) {
119 |
120 | angle = getAutoRotationAngle();
121 |
122 | }
123 |
124 | phiDelta -= angle;
125 |
126 | };
127 |
128 | // pass in distance in world space to move left
129 | this.panLeft = function ( distance ) {
130 |
131 | var panOffset = new THREE.Vector3();
132 | var te = this.object.matrix.elements;
133 | // get X column of matrix
134 | panOffset.set( te[0], te[1], te[2] );
135 | panOffset.multiplyScalar(-distance);
136 |
137 | pan.add( panOffset );
138 |
139 | };
140 |
141 | // pass in distance in world space to move up
142 | this.panUp = function ( distance ) {
143 |
144 | var panOffset = new THREE.Vector3();
145 | var te = this.object.matrix.elements;
146 | // get Y column of matrix
147 | panOffset.set( te[4], te[5], te[6] );
148 | panOffset.multiplyScalar(distance);
149 |
150 | pan.add( panOffset );
151 | };
152 |
153 | // main entry point; pass in Vector2 of change desired in pixel space,
154 | // right and down are positive
155 | this.pan = function ( delta ) {
156 |
157 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
158 |
159 | if ( scope.object.fov !== undefined ) {
160 |
161 | // perspective
162 | var position = scope.object.position;
163 | var offset = position.clone().sub( scope.target );
164 | var targetDistance = offset.length();
165 |
166 | // half of the fov is center to top of screen
167 | targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 );
168 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
169 | scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight );
170 | scope.panUp( 2 * delta.y * targetDistance / element.clientHeight );
171 |
172 | } else if ( scope.object.top !== undefined ) {
173 |
174 | // orthographic
175 | scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth );
176 | scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight );
177 |
178 | } else {
179 |
180 | // camera neither orthographic or perspective - warn user
181 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
182 |
183 | }
184 |
185 | };
186 |
187 | this.dollyIn = function ( dollyScale ) {
188 |
189 | if ( dollyScale === undefined ) {
190 |
191 | dollyScale = getZoomScale();
192 |
193 | }
194 |
195 | scale /= dollyScale;
196 |
197 | };
198 |
199 | this.dollyOut = function ( dollyScale ) {
200 |
201 | if ( dollyScale === undefined ) {
202 |
203 | dollyScale = getZoomScale();
204 |
205 | }
206 |
207 | scale *= dollyScale;
208 |
209 | };
210 |
211 | this.update = function () {
212 |
213 | var position = this.object.position;
214 | var offset = position.clone().sub( this.target );
215 |
216 | // angle from z-axis around y-axis
217 |
218 | var theta = Math.atan2( offset.x, offset.z );
219 |
220 | // angle from y-axis
221 |
222 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
223 |
224 | if ( this.autoRotate ) {
225 |
226 | this.rotateLeft( getAutoRotationAngle() );
227 |
228 | }
229 |
230 | theta += thetaDelta;
231 | phi += phiDelta;
232 |
233 | // restrict phi to be between desired limits
234 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
235 |
236 | // restrict phi to be betwee EPS and PI-EPS
237 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
238 |
239 | var radius = offset.length() * scale;
240 |
241 | // restrict radius to be between desired limits
242 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
243 |
244 | // move target to panned location
245 | this.target.add( pan );
246 |
247 | offset.x = radius * Math.sin( phi ) * Math.sin( theta );
248 | offset.y = radius * Math.cos( phi );
249 | offset.z = radius * Math.sin( phi ) * Math.cos( theta );
250 |
251 | position.copy( this.target ).add( offset );
252 |
253 | this.object.lookAt( this.target );
254 |
255 | thetaDelta = 0;
256 | phiDelta = 0;
257 | scale = 1;
258 | pan.set(0,0,0);
259 |
260 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
261 |
262 | this.dispatchEvent( changeEvent );
263 |
264 | lastPosition.copy( this.object.position );
265 |
266 | }
267 |
268 | };
269 |
270 |
271 | function getAutoRotationAngle() {
272 |
273 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
274 |
275 | }
276 |
277 | function getZoomScale() {
278 |
279 | return Math.pow( 0.95, scope.zoomSpeed );
280 |
281 | }
282 |
283 | function onMouseDown( event ) {
284 |
285 | if ( scope.enabled === false ) { return; }
286 | event.preventDefault();
287 |
288 | if ( event.button === 0 ) {
289 | if ( scope.noRotate === true ) { return; }
290 |
291 | state = STATE.ROTATE;
292 |
293 | rotateStart.set( event.clientX, event.clientY );
294 |
295 | } else if ( event.button === 1 ) {
296 | if ( scope.noZoom === true ) { return; }
297 |
298 | state = STATE.DOLLY;
299 |
300 | dollyStart.set( event.clientX, event.clientY );
301 |
302 | } else if ( event.button === 2 ) {
303 | if ( scope.noPan === true ) { return; }
304 |
305 | state = STATE.PAN;
306 |
307 | panStart.set( event.clientX, event.clientY );
308 |
309 | }
310 |
311 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
312 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
313 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
314 |
315 | }
316 |
317 | function onMouseMove( event ) {
318 |
319 | if ( scope.enabled === false ) return;
320 |
321 | event.preventDefault();
322 |
323 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
324 |
325 | if ( state === STATE.ROTATE ) {
326 |
327 | if ( scope.noRotate === true ) return;
328 |
329 | rotateEnd.set( event.clientX, event.clientY );
330 | rotateDelta.subVectors( rotateEnd, rotateStart );
331 |
332 | // rotating across whole screen goes 360 degrees around
333 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
334 | // rotating up and down along whole screen attempts to go 360, but limited to 180
335 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
336 |
337 | rotateStart.copy( rotateEnd );
338 |
339 | } else if ( state === STATE.DOLLY ) {
340 |
341 | if ( scope.noZoom === true ) return;
342 |
343 | dollyEnd.set( event.clientX, event.clientY );
344 | dollyDelta.subVectors( dollyEnd, dollyStart );
345 |
346 | if ( dollyDelta.y > 0 ) {
347 |
348 | scope.dollyIn();
349 |
350 | } else {
351 |
352 | scope.dollyOut();
353 |
354 | }
355 |
356 | dollyStart.copy( dollyEnd );
357 |
358 | } else if ( state === STATE.PAN ) {
359 |
360 | if ( scope.noPan === true ) return;
361 |
362 | panEnd.set( event.clientX, event.clientY );
363 | panDelta.subVectors( panEnd, panStart );
364 |
365 | scope.pan( panDelta );
366 |
367 | panStart.copy( panEnd );
368 |
369 | }
370 |
371 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
372 | scope.update();
373 |
374 | }
375 |
376 | function onMouseUp( /* event */ ) {
377 |
378 | if ( scope.enabled === false ) return;
379 |
380 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
381 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
382 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
383 |
384 | state = STATE.NONE;
385 |
386 | }
387 |
388 | function onMouseWheel( event ) {
389 |
390 | if ( scope.enabled === false || scope.noZoom === true ) return;
391 |
392 | var delta = 0;
393 |
394 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
395 |
396 | delta = event.wheelDelta;
397 |
398 | } else if ( event.detail ) { // Firefox
399 |
400 | delta = - event.detail;
401 |
402 | }
403 |
404 | if ( delta > 0 ) {
405 |
406 | scope.dollyOut();
407 |
408 | } else {
409 |
410 | scope.dollyIn();
411 |
412 | }
413 |
414 | }
415 |
416 | function onKeyDown( event ) {
417 |
418 | if ( scope.enabled === false ) { return; }
419 | if ( scope.noKeys === true ) { return; }
420 | if ( scope.noPan === true ) { return; }
421 |
422 | // pan a pixel - I guess for precise positioning?
423 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
424 | var needUpdate = false;
425 |
426 | switch ( event.keyCode ) {
427 |
428 | case scope.keys.UP:
429 | scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) );
430 | needUpdate = true;
431 | break;
432 | case scope.keys.BOTTOM:
433 | scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) );
434 | needUpdate = true;
435 | break;
436 | case scope.keys.LEFT:
437 | scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) );
438 | needUpdate = true;
439 | break;
440 | case scope.keys.RIGHT:
441 | scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) );
442 | needUpdate = true;
443 | break;
444 | }
445 |
446 | // Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
447 | if ( needUpdate ) {
448 |
449 | scope.update();
450 |
451 | }
452 |
453 | }
454 |
455 | function touchstart( event ) {
456 |
457 | if ( scope.enabled === false ) { return; }
458 |
459 | switch ( event.touches.length ) {
460 |
461 | case 1: // one-fingered touch: rotate
462 | if ( scope.noRotate === true ) { return; }
463 |
464 | state = STATE.TOUCH_ROTATE;
465 |
466 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
467 | break;
468 |
469 | case 2: // two-fingered touch: dolly
470 | if ( scope.noZoom === true ) { return; }
471 |
472 | state = STATE.TOUCH_DOLLY;
473 |
474 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
475 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
476 | var distance = Math.sqrt( dx * dx + dy * dy );
477 | dollyStart.set( 0, distance );
478 | break;
479 |
480 | case 3: // three-fingered touch: pan
481 | if ( scope.noPan === true ) { return; }
482 |
483 | state = STATE.TOUCH_PAN;
484 |
485 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
486 | break;
487 |
488 | default:
489 | state = STATE.NONE;
490 |
491 | }
492 | }
493 |
494 | function touchmove( event ) {
495 |
496 | if ( scope.enabled === false ) { return; }
497 |
498 | event.preventDefault();
499 | event.stopPropagation();
500 |
501 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
502 |
503 | switch ( event.touches.length ) {
504 |
505 | case 1: // one-fingered touch: rotate
506 | if ( scope.noRotate === true ) { return; }
507 | if ( state !== STATE.TOUCH_ROTATE ) { return; }
508 |
509 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
510 | rotateDelta.subVectors( rotateEnd, rotateStart );
511 |
512 | // rotating across whole screen goes 360 degrees around
513 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
514 | // rotating up and down along whole screen attempts to go 360, but limited to 180
515 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
516 |
517 | rotateStart.copy( rotateEnd );
518 | break;
519 |
520 | case 2: // two-fingered touch: dolly
521 | if ( scope.noZoom === true ) { return; }
522 | if ( state !== STATE.TOUCH_DOLLY ) { return; }
523 |
524 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
525 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
526 | var distance = Math.sqrt( dx * dx + dy * dy );
527 |
528 | dollyEnd.set( 0, distance );
529 | dollyDelta.subVectors( dollyEnd, dollyStart );
530 |
531 | if ( dollyDelta.y > 0 ) {
532 |
533 | scope.dollyOut();
534 |
535 | } else {
536 |
537 | scope.dollyIn();
538 |
539 | }
540 |
541 | dollyStart.copy( dollyEnd );
542 | break;
543 |
544 | case 3: // three-fingered touch: pan
545 | if ( scope.noPan === true ) { return; }
546 | if ( state !== STATE.TOUCH_PAN ) { return; }
547 |
548 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
549 | panDelta.subVectors( panEnd, panStart );
550 |
551 | scope.pan( panDelta );
552 |
553 | panStart.copy( panEnd );
554 | break;
555 |
556 | default:
557 | state = STATE.NONE;
558 |
559 | }
560 |
561 | }
562 |
563 | function touchend( /* event */ ) {
564 |
565 | if ( scope.enabled === false ) { return; }
566 |
567 | state = STATE.NONE;
568 | }
569 |
570 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
571 | this.domElement.addEventListener( 'mousedown', onMouseDown, false );
572 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
573 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
574 |
575 | this.domElement.addEventListener( 'keydown', onKeyDown, false );
576 |
577 | this.domElement.addEventListener( 'touchstart', touchstart, false );
578 | this.domElement.addEventListener( 'touchend', touchend, false );
579 | this.domElement.addEventListener( 'touchmove', touchmove, false );
580 |
581 | };
582 |
583 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
--------------------------------------------------------------------------------
/public/js/lightbox.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Lightbox v2.10.0
3 | * by Lokesh Dhakar
4 | *
5 | * More info:
6 | * http://lokeshdhakar.com/projects/lightbox2/
7 | *
8 | * Copyright 2007, 2018 Lokesh Dhakar
9 | * Released under the MIT license
10 | * https://github.com/lokesh/lightbox2/blob/master/LICENSE
11 | *
12 | * @preserve
13 | */
14 |
15 | // Uses Node, AMD or browser globals to create a module.
16 | (function (root, factory) {
17 | if (typeof define === 'function' && define.amd) {
18 | // AMD. Register as an anonymous module.
19 | define(['jquery'], factory);
20 | } else if (typeof exports === 'object') {
21 | // Node. Does not work with strict CommonJS, but
22 | // only CommonJS-like environments that support module.exports,
23 | // like Node.
24 | module.exports = factory(require('jquery'));
25 | } else {
26 | // Browser globals (root is window)
27 | root.lightbox = factory(root.jQuery);
28 | }
29 | }(this, function ($) {
30 |
31 | function Lightbox(options) {
32 | this.album = [];
33 | this.currentImageIndex = void 0;
34 | this.init();
35 |
36 | // options
37 | this.options = $.extend({}, this.constructor.defaults);
38 | this.option(options);
39 | }
40 |
41 | // Descriptions of all options available on the demo site:
42 | // http://lokeshdhakar.com/projects/lightbox2/index.html#options
43 | Lightbox.defaults = {
44 | albumLabel: 'Image %1 of %2',
45 | alwaysShowNavOnTouchDevices: false,
46 | fadeDuration: 600,
47 | fitImagesInViewport: true,
48 | imageFadeDuration: 600,
49 | // maxWidth: 800,
50 | // maxHeight: 600,
51 | positionFromTop: 50,
52 | resizeDuration: 700,
53 | showImageNumberLabel: true,
54 | wrapAround: false,
55 | disableScrolling: false,
56 | /*
57 | Sanitize Title
58 | If the caption data is trusted, for example you are hardcoding it in, then leave this to false.
59 | This will free you to add html tags, such as links, in the caption.
60 |
61 | If the caption data is user submitted or from some other untrusted source, then set this to true
62 | to prevent xss and other injection attacks.
63 | */
64 | sanitizeTitle: false
65 | };
66 |
67 | Lightbox.prototype.option = function(options) {
68 | $.extend(this.options, options);
69 | };
70 |
71 | Lightbox.prototype.imageCountLabel = function(currentImageNum, totalImages) {
72 | return this.options.albumLabel.replace(/%1/g, currentImageNum).replace(/%2/g, totalImages);
73 | };
74 |
75 | Lightbox.prototype.init = function() {
76 | var self = this;
77 | // Both enable and build methods require the body tag to be in the DOM.
78 | $(document).ready(function() {
79 | self.enable();
80 | self.build();
81 | });
82 | };
83 |
84 | // Loop through anchors and areamaps looking for either data-lightbox attributes or rel attributes
85 | // that contain 'lightbox'. When these are clicked, start lightbox.
86 | Lightbox.prototype.enable = function() {
87 | var self = this;
88 | //添加touchstart解决ios上面无法现实大图的问题@hillock
89 | $('body').on('click touchstart', 'a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]', function(event) {
90 | self.start($(event.currentTarget));
91 | return false;
92 | });
93 | };
94 |
95 | // Build html for the lightbox and the overlay.
96 | // Attach event handlers to the new DOM elements. click click click
97 | Lightbox.prototype.build = function() {
98 | if ($('#lightbox').length > 0) {
99 | return;
100 | }
101 |
102 | var self = this;
103 | $('').appendTo($('body'));
104 |
105 | // Cache jQuery objects
106 | this.$lightbox = $('#lightbox');
107 | this.$overlay = $('#lightboxOverlay');
108 | this.$outerContainer = this.$lightbox.find('.lb-outerContainer');
109 | this.$container = this.$lightbox.find('.lb-container');
110 | this.$image = this.$lightbox.find('.lb-image');
111 | this.$nav = this.$lightbox.find('.lb-nav');
112 |
113 | // Store css values for future lookup
114 | this.containerPadding = {
115 | top: parseInt(this.$container.css('padding-top'), 10),
116 | right: parseInt(this.$container.css('padding-right'), 10),
117 | bottom: parseInt(this.$container.css('padding-bottom'), 10),
118 | left: parseInt(this.$container.css('padding-left'), 10)
119 | };
120 |
121 | this.imageBorderWidth = {
122 | top: parseInt(this.$image.css('border-top-width'), 10),
123 | right: parseInt(this.$image.css('border-right-width'), 10),
124 | bottom: parseInt(this.$image.css('border-bottom-width'), 10),
125 | left: parseInt(this.$image.css('border-left-width'), 10)
126 | };
127 |
128 | // Attach event handlers to the newly minted DOM elements
129 | this.$overlay.hide().on('click', function() {
130 | self.end();
131 | return false;
132 | });
133 |
134 | this.$lightbox.hide().on('click', function(event) {
135 | if ($(event.target).attr('id') === 'lightbox') {
136 | self.end();
137 | }
138 | return false;
139 | });
140 |
141 | this.$outerContainer.on('click', function(event) {
142 | if ($(event.target).attr('id') === 'lightbox') {
143 | self.end();
144 | }
145 | return false;
146 | });
147 |
148 | this.$lightbox.find('.lb-prev').on('click', function() {
149 | if (self.currentImageIndex === 0) {
150 | self.changeImage(self.album.length - 1);
151 | } else {
152 | self.changeImage(self.currentImageIndex - 1);
153 | }
154 | return false;
155 | });
156 |
157 | this.$lightbox.find('.lb-next').on('click', function() {
158 | if (self.currentImageIndex === self.album.length - 1) {
159 | self.changeImage(0);
160 | } else {
161 | self.changeImage(self.currentImageIndex + 1);
162 | }
163 | return false;
164 | });
165 |
166 | /*
167 | Show context menu for image on right-click
168 |
169 | There is a div containing the navigation that spans the entire image and lives above of it. If
170 | you right-click, you are right clicking this div and not the image. This prevents users from
171 | saving the image or using other context menu actions with the image.
172 |
173 | To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we
174 | set pointer-events to none on the nav div. This is so that the upcoming right-click event on
175 | the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs
176 | we set the pointer events back to auto for the nav div so it can capture hover and left-click
177 | events as usual.
178 | */
179 | this.$nav.on('mousedown', function(event) {
180 | if (event.which === 3) {
181 | self.$nav.css('pointer-events', 'none');
182 |
183 | self.$lightbox.one('contextmenu', function() {
184 | setTimeout(function() {
185 | this.$nav.css('pointer-events', 'auto');
186 | }.bind(self), 0);
187 | });
188 | }
189 | });
190 |
191 |
192 | this.$lightbox.find('.lb-loader, .lb-close').on('click', function() {
193 | self.end();
194 | return false;
195 | });
196 | };
197 |
198 | // Show overlay and lightbox. If the image is part of a set, add siblings to album array.
199 | Lightbox.prototype.start = function($link) {
200 | var self = this;
201 | var $window = $(window);
202 |
203 | $window.on('resize', $.proxy(this.sizeOverlay, this));
204 |
205 | $('select, object, embed').css({
206 | visibility: 'hidden'
207 | });
208 |
209 | this.sizeOverlay();
210 |
211 | this.album = [];
212 | var imageNumber = 0;
213 |
214 | function addToAlbum($link) {
215 | self.album.push({
216 | alt: $link.attr('data-alt'),
217 | link: $link.attr('href'),
218 | title: $link.attr('data-title') || $link.attr('title')
219 | });
220 | }
221 |
222 | // Support both data-lightbox attribute and rel attribute implementations
223 | var dataLightboxValue = $link.attr('data-lightbox');
224 | var $links;
225 |
226 | if (dataLightboxValue) {
227 | $links = $($link.prop('tagName') + '[data-lightbox="' + dataLightboxValue + '"]');
228 | for (var i = 0; i < $links.length; i = ++i) {
229 | addToAlbum($($links[i]));
230 | if ($links[i] === $link[0]) {
231 | imageNumber = i;
232 | }
233 | }
234 | } else {
235 | if ($link.attr('rel') === 'lightbox') {
236 | // If image is not part of a set
237 | addToAlbum($link);
238 | } else {
239 | // If image is part of a set
240 | $links = $($link.prop('tagName') + '[rel="' + $link.attr('rel') + '"]');
241 | for (var j = 0; j < $links.length; j = ++j) {
242 | addToAlbum($($links[j]));
243 | if ($links[j] === $link[0]) {
244 | imageNumber = j;
245 | }
246 | }
247 | }
248 | }
249 |
250 | // Position Lightbox
251 | var top = $window.scrollTop() + this.options.positionFromTop;
252 | var left = $window.scrollLeft();
253 | this.$lightbox.css({
254 | top: top + 'px',
255 | left: left + 'px'
256 | }).fadeIn(this.options.fadeDuration);
257 |
258 | // Disable scrolling of the page while open
259 | if (this.options.disableScrolling) {
260 | $('html').addClass('lb-disable-scrolling');
261 | }
262 |
263 | this.changeImage(imageNumber);
264 | };
265 |
266 | // Hide most UI elements in preparation for the animated resizing of the lightbox.
267 | Lightbox.prototype.changeImage = function(imageNumber) {
268 | var self = this;
269 |
270 | this.disableKeyboardNav();
271 | var $image = this.$lightbox.find('.lb-image');
272 |
273 | this.$overlay.fadeIn(this.options.fadeDuration);
274 |
275 | $('.lb-loader').fadeIn('slow');
276 | this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide();
277 |
278 | this.$outerContainer.addClass('animating');
279 |
280 | // When image to show is preloaded, we send the width and height to sizeContainer()
281 | var preloader = new Image();
282 | preloader.onload = function() {
283 | var $preloader;
284 | var imageHeight;
285 | var imageWidth;
286 | var maxImageHeight;
287 | var maxImageWidth;
288 | var windowHeight;
289 | var windowWidth;
290 |
291 | $image.attr({
292 | 'alt': self.album[imageNumber].alt,
293 | 'src': self.album[imageNumber].link
294 | });
295 |
296 | $preloader = $(preloader);
297 |
298 | $image.width(preloader.width);
299 | $image.height(preloader.height);
300 |
301 | if (self.options.fitImagesInViewport) {
302 | // Fit image inside the viewport.
303 | // Take into account the border around the image and an additional 10px gutter on each side.
304 |
305 | windowWidth = $(window).width();
306 | windowHeight = $(window).height();
307 | maxImageWidth = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20;
308 | maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - 120;
309 |
310 | // Check if image size is larger then maxWidth|maxHeight in settings
311 | if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) {
312 | maxImageWidth = self.options.maxWidth;
313 | }
314 | if (self.options.maxHeight && self.options.maxHeight < maxImageWidth) {
315 | maxImageHeight = self.options.maxHeight;
316 | }
317 |
318 | // Is the current image's width or height is greater than the maxImageWidth or maxImageHeight
319 | // option than we need to size down while maintaining the aspect ratio.
320 | if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) {
321 | if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) {
322 | imageWidth = maxImageWidth;
323 | imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10);
324 | $image.width(imageWidth);
325 | $image.height(imageHeight);
326 | } else {
327 | imageHeight = maxImageHeight;
328 | imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10);
329 | $image.width(imageWidth);
330 | $image.height(imageHeight);
331 | }
332 | }
333 | }
334 | self.sizeContainer($image.width(), $image.height());
335 | };
336 |
337 | preloader.src = this.album[imageNumber].link;
338 | this.currentImageIndex = imageNumber;
339 | };
340 |
341 | // Stretch overlay to fit the viewport
342 | Lightbox.prototype.sizeOverlay = function() {
343 | this.$overlay
344 | .width($(document).width())
345 | .height($(document).height());
346 | };
347 |
348 | // Animate the size of the lightbox to fit the image we are showing
349 | Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) {
350 | var self = this;
351 |
352 | var oldWidth = this.$outerContainer.outerWidth();
353 | var oldHeight = this.$outerContainer.outerHeight();
354 | var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right;
355 | var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom;
356 |
357 | function postResize() {
358 | self.$lightbox.find('.lb-dataContainer').width(newWidth);
359 | self.$lightbox.find('.lb-prevLink').height(newHeight);
360 | self.$lightbox.find('.lb-nextLink').height(newHeight);
361 | self.showImage();
362 | }
363 |
364 | if (oldWidth !== newWidth || oldHeight !== newHeight) {
365 | this.$outerContainer.animate({
366 | width: newWidth,
367 | height: newHeight
368 | }, this.options.resizeDuration, 'swing', function() {
369 | postResize();
370 | });
371 | } else {
372 | postResize();
373 | }
374 | };
375 |
376 | // Display the image and its details and begin preload neighboring images.
377 | Lightbox.prototype.showImage = function() {
378 | this.$lightbox.find('.lb-loader').stop(true).hide();
379 | this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration);
380 |
381 | this.updateNav();
382 | this.updateDetails();
383 | this.preloadNeighboringImages();
384 | this.enableKeyboardNav();
385 | };
386 |
387 | // Display previous and next navigation if appropriate.
388 | Lightbox.prototype.updateNav = function() {
389 | // Check to see if the browser supports touch events. If so, we take the conservative approach
390 | // and assume that mouse hover events are not supported and always show prev/next navigation
391 | // arrows in image sets.
392 | var alwaysShowNav = false;
393 | try {
394 | document.createEvent('TouchEvent');
395 | alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false;
396 | } catch (e) {}
397 |
398 | this.$lightbox.find('.lb-nav').show();
399 |
400 | if (this.album.length > 1) {
401 | if (this.options.wrapAround) {
402 | if (alwaysShowNav) {
403 | this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1');
404 | }
405 | this.$lightbox.find('.lb-prev, .lb-next').show();
406 | } else {
407 | if (this.currentImageIndex > 0) {
408 | this.$lightbox.find('.lb-prev').show();
409 | if (alwaysShowNav) {
410 | this.$lightbox.find('.lb-prev').css('opacity', '1');
411 | }
412 | }
413 | if (this.currentImageIndex < this.album.length - 1) {
414 | this.$lightbox.find('.lb-next').show();
415 | if (alwaysShowNav) {
416 | this.$lightbox.find('.lb-next').css('opacity', '1');
417 | }
418 | }
419 | }
420 | }
421 | };
422 |
423 | // Display caption, image number, and closing button.
424 | Lightbox.prototype.updateDetails = function() {
425 | var self = this;
426 |
427 | // Enable anchor clicks in the injected caption html.
428 | // Thanks Nate Wright for the fix. @https://github.com/NateWr
429 | if (typeof this.album[this.currentImageIndex].title !== 'undefined' &&
430 | this.album[this.currentImageIndex].title !== '') {
431 | var $caption = this.$lightbox.find('.lb-caption');
432 | if (this.options.sanitizeTitle) {
433 | $caption.text(this.album[this.currentImageIndex].title);
434 | } else {
435 | $caption.html(this.album[this.currentImageIndex].title);
436 | }
437 | $caption.fadeIn('fast')
438 | .find('a').on('click', function(event) {
439 | if ($(this).attr('target') !== undefined) {
440 | window.open($(this).attr('href'), $(this).attr('target'));
441 | } else {
442 | location.href = $(this).attr('href');
443 | }
444 | });
445 | }
446 |
447 | if (this.album.length > 1 && this.options.showImageNumberLabel) {
448 | var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length);
449 | this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast');
450 | } else {
451 | this.$lightbox.find('.lb-number').hide();
452 | }
453 |
454 | this.$outerContainer.removeClass('animating');
455 |
456 | this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() {
457 | return self.sizeOverlay();
458 | });
459 | };
460 |
461 | // Preload previous and next images in set.
462 | Lightbox.prototype.preloadNeighboringImages = function() {
463 | if (this.album.length > this.currentImageIndex + 1) {
464 | var preloadNext = new Image();
465 | preloadNext.src = this.album[this.currentImageIndex + 1].link;
466 | }
467 | if (this.currentImageIndex > 0) {
468 | var preloadPrev = new Image();
469 | preloadPrev.src = this.album[this.currentImageIndex - 1].link;
470 | }
471 | };
472 |
473 | Lightbox.prototype.enableKeyboardNav = function() {
474 | $(document).on('keyup.keyboard', $.proxy(this.keyboardAction, this));
475 | };
476 |
477 | Lightbox.prototype.disableKeyboardNav = function() {
478 | $(document).off('.keyboard');
479 | };
480 |
481 | Lightbox.prototype.keyboardAction = function(event) {
482 | var KEYCODE_ESC = 27;
483 | var KEYCODE_LEFTARROW = 37;
484 | var KEYCODE_RIGHTARROW = 39;
485 |
486 | var keycode = event.keyCode;
487 | var key = String.fromCharCode(keycode).toLowerCase();
488 | if (keycode === KEYCODE_ESC || key.match(/x|o|c/)) {
489 | this.end();
490 | } else if (key === 'p' || keycode === KEYCODE_LEFTARROW) {
491 | if (this.currentImageIndex !== 0) {
492 | this.changeImage(this.currentImageIndex - 1);
493 | } else if (this.options.wrapAround && this.album.length > 1) {
494 | this.changeImage(this.album.length - 1);
495 | }
496 | } else if (key === 'n' || keycode === KEYCODE_RIGHTARROW) {
497 | if (this.currentImageIndex !== this.album.length - 1) {
498 | this.changeImage(this.currentImageIndex + 1);
499 | } else if (this.options.wrapAround && this.album.length > 1) {
500 | this.changeImage(0);
501 | }
502 | }
503 | };
504 |
505 | // Closing time. :-(
506 | Lightbox.prototype.end = function() {
507 | this.disableKeyboardNav();
508 | $(window).off('resize', this.sizeOverlay);
509 | this.$lightbox.fadeOut(this.options.fadeDuration);
510 | this.$overlay.fadeOut(this.options.fadeDuration);
511 | $('select, object, embed').css({
512 | visibility: 'visible'
513 | });
514 | if (this.options.disableScrolling) {
515 | $('html').removeClass('lb-disable-scrolling');
516 | }
517 | };
518 |
519 | return new Lightbox();
520 | }));
521 |
--------------------------------------------------------------------------------