add vr player

This commit is contained in:
jiegeaiai 2025-02-08 00:55:28 +08:00
parent 1b26562123
commit 7d2d3973bd
40 changed files with 128594 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
.DS_Store
vr/node_modules
vr//dist
vr/tests/e2e/videos/
vr/tests/e2e/screenshots/
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

Binary file not shown.

BIN
test_data/08-26-17.mp4 Normal file

Binary file not shown.

3
vr/.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

5
vr/.editorconfig Normal file
View File

@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

28
vr/.eslintrc.js Normal file
View File

@ -0,0 +1,28 @@
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/essential',
'@vue/standard'
],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
jest: true
}
}
]
}

24
vr/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
.DS_Store
node_modules
/dist
/tests/e2e/videos/
/tests/e2e/screenshots/
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

36
vr/README.md Normal file
View File

@ -0,0 +1,36 @@
# vue-vr
基于threejs,html5,hlsjs,flvjs编写的全景播放器播放普通视频hls及flv直播流
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your unit tests
```
npm run test:unit
```
### Run your end-to-end tests
```
npm run test:e2e
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

45
vr/aisettings Normal file
View File

@ -0,0 +1,45 @@
{
"model": {
"provider": "anthropic",
"name": "claude-3-sonnet",
"temperature": 0.7,
"contextLength": 200000,
"maxTokens": 4096
},
"behavior": {
"role": "Expert software developer and technical consultant",
"expertise": [
"Video player development",
"WebGL and 3D graphics",
"JavaScript/TypeScript",
"Web development",
"Accessibility features",
"Media streaming"
],
"communication": {
"style": "Professional and technical",
"format": "Clear explanations with code examples",
"codeStyle": {
"language": "JavaScript",
"formatting": "Standard JS",
"comments": true
}
}
},
"capabilities": {
"codeAnalysis": true,
"debugging": true,
"refactoring": true,
"bestPractices": true,
"documentation": true
},
"preferences": {
"outputFormat": "markdown",
"codeHighlighting": true,
"includeExplanations": true,
"showLineNumbers": true
}
}

11
vr/babel.config.js Normal file
View File

@ -0,0 +1,11 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
compact: true,
// 增加 videojs-vr.js 的特殊处理
overrides: [{
test: /videojs-vr\.js$/,
compact: false
}]
}

3
vr/cypress.json Normal file
View File

@ -0,0 +1,3 @@
{
"pluginsFile": "tests/e2e/plugins/index.js"
}

0
vr/debug.log Normal file
View File

3
vr/jest.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
preset: '@vue/cli-plugin-unit-jest'
}

26191
vr/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

47
vr/package.json Normal file
View File

@ -0,0 +1,47 @@
{
"name": "vr",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"test:e2e": "vue-cli-service test:e2e",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.12.0",
"deasync": "^0.1.30",
"flv.js": "^1.5.0",
"hls.js": "^0.13.2",
"three": "^0.116.1",
"three-orbit-controls": "^82.1.0",
"videojs-flash": "^2.2.1",
"videojs-panorama": "^0.1.7",
"videojs-vr": "^1.7.1",
"vue": "^2.6.12",
"vue-router": "^3.5.1",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.3.1",
"@vue/cli-plugin-e2e-cypress": "^4.3.1",
"@vue/cli-plugin-eslint": "^4.3.1",
"@vue/cli-plugin-router": "^4.3.1",
"@vue/cli-plugin-unit-jest": "^4.3.1",
"@vue/cli-plugin-vuex": "^4.3.1",
"@vue/cli-service": "^4.3.1",
"@vue/eslint-config-standard": "^5.1.2",
"@vue/test-utils": "^1.0.0-beta.31",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-standard": "^4.1.0",
"eslint-plugin-vue": "^6.2.2",
"sass": "^1.32.13",
"sass-loader": "^10.1.1",
"vue-template-compiler": "^2.6.12"
}
}

BIN
vr/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

19
vr/public/index.html Normal file
View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<link href="https://vjs.zencdn.net/7.7.6/video-js.css" rel="stylesheet" />
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<!-- <script src="https://threejs.org/build/three.js"></script> -->
</body>
</html>

14
vr/src/App.vue Normal file
View File

@ -0,0 +1,14 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<style lang="scss">
@import url('http://at.alicdn.com/t/font_1857388_u76j431l9gp.css');
@import "./assets/css/reset.scss";
#app{
height: 100%;
background: rgb(75,75,75);
}
</style>

View File

@ -0,0 +1,465 @@
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in
* IE on Windows Phone and in iOS.
*/
html {
height: 100%;
line-height: 1.15; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers (opinionated).
*/
body {
margin: 0;
height: 100%;
}
/**
* Add the correct display in IE 9-.
*/
article,
aside,
footer,
header,
nav,
section {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* Add the correct display in IE 9-.
* 1. Add the correct display in IE.
*/
figcaption,
figure,
main { /* 1 */
display: block;
}
/**
* Add the correct margin in IE 8.
*/
figure {
margin: 1em 40px;
}
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* 1. Remove the gray background on active links in IE 10.
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/
a {
background-color: transparent; /* 1 */
-webkit-text-decoration-skip: objects; /* 2 */
}
/**
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/
b,
strong {
font-weight: inherit;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font style in Android 4.3-.
*/
dfn {
font-style: italic;
}
/**
* Add the correct background and color in IE 9-.
*/
mark {
background-color: #ff0;
color: #000;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
audio,
video {
display: inline-block;
}
/**
* Add the correct display in iOS 4-7.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Remove the border on images inside links in IE 10-.
*/
img {
border-style: none;
}
/**
* Hide the overflow in IE.
*/
svg:not(:root) {
overflow: hidden;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers (opinionated).
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: sans-serif; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/
button,
html [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; /* 2 */
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* 1. Add the correct display in IE 9-.
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Remove the default vertical scrollbar in IE.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10-.
* 2. Remove the padding in IE 10-.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in IE 9-.
* 1. Add the correct display in Edge, IE, and Firefox.
*/
details, /* 1 */
menu {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Scripting
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
canvas {
display: inline-block;
}
/**
* Add the correct display in IE.
*/
template {
display: none;
}
/* Hidden
========================================================================== */
/**
* Add the correct display in IE 10-.
*/
[hidden] {
display: none;
}
/**
* 重置button及input预置样式
*/
button,
input { /* 1 */
all: unset;
}
button{
text-align:center;
&:hover{
cursor: pointer;
}
}

View File

@ -0,0 +1,34 @@
.video-js .vjs-big-vr-play-button {
width: 100px;
height: 100px;
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='360' viewBox='0 0 360 360'%3E%3Cpath fill='%23FFF' d='M334.883 275.78l-6.374-36.198-6.375-36.2-28.16 23.62-28.164 23.62 25.837 9.41C266.247 296.544 224 320.5 176.25 320.5c-77.47 0-140.5-63.03-140.5-140.5 0-77.472 63.03-140.5 140.5-140.5 53.428 0 99.98 29.978 123.733 73.993l13.304-6.923C287.025 57.76 235.45 24.5 176.25 24.5c-85.743 0-155.5 69.757-155.5 155.5 0 85.742 69.757 155.5 155.5 155.5 54.253 0 102.09-27.94 129.922-70.177l28.71 10.457z'/%3E%3Cpath fill='%23FFF' d='M314.492 175.167c-12.98 0-23.54-10.56-23.54-23.54s10.56-23.54 23.54-23.54c12.98 0 23.54 10.56 23.54 23.54s-10.56 23.54-23.54 23.54zm0-38.08c-8.018 0-14.54 6.522-14.54 14.54s6.522 14.54 14.54 14.54c8.017 0 14.54-6.522 14.54-14.54s-6.523-14.54-14.54-14.54z'/%3E%3Cg fill='%23FFF'%3E%3Cpath d='M88.76 173.102h9.395c4.74-.042 8.495-1.27 11.268-3.682 2.77-2.412 4.157-5.903 4.157-10.474 0-4.4-1.153-7.817-3.46-10.25-2.307-2.434-5.83-3.65-10.568-3.65-4.147 0-7.554 1.195-10.22 3.585-2.666 2.392-4 5.514-4 9.364H69.908c0-4.74 1.26-9.055 3.776-12.95 2.518-3.892 6.03-6.928 10.537-9.108 4.508-2.18 9.554-3.27 15.14-3.27 9.225 0 16.472 2.318 21.74 6.952 5.27 4.634 7.903 11.077 7.903 19.33 0 4.147-1.323 8.05-3.967 11.71-2.646 3.66-6.062 6.422-10.252 8.284 5.078 1.736 8.94 4.465 11.584 8.19s3.968 8.166 3.968 13.33c0 8.294-2.847 14.895-8.538 19.804s-13.17 7.363-22.438 7.363c-8.887 0-16.166-2.37-21.836-7.11-5.67-4.74-8.506-11.045-8.506-18.916h15.425c0 4.062 1.365 7.363 4.094 9.902 2.73 2.54 6.4 3.81 11.014 3.81 4.782 0 8.55-1.27 11.3-3.81s4.126-6.22 4.126-11.045c0-4.865-1.44-8.61-4.316-11.235-2.878-2.623-7.152-3.936-12.822-3.936H88.76V173.1zM187.598 133.493v12.76h-1.904c-8.633.126-15.53 2.497-20.693 7.108-5.162 4.614-8.23 11.152-9.203 19.615 4.95-5.205 11.277-7.808 18.98-7.808 8.166 0 14.608 2.878 19.328 8.633 4.718 5.755 7.077 13.182 7.077 22.28 0 9.395-2.76 17.002-8.284 22.82-5.52 5.818-12.77 8.73-21.74 8.73-9.226 0-16.705-3.407-22.44-10.222-5.733-6.812-8.6-15.742-8.6-26.787v-5.267c0-16.208 3.945-28.903 11.84-38.086 7.89-9.182 19.242-13.774 34.054-13.774h1.586zM171.03 177.61c-3.386 0-6.485.95-9.3 2.855-2.814 1.904-4.877 4.443-6.188 7.617v4.697c0 6.854 1.438 12.304 4.316 16.345 2.877 4.04 6.602 6.062 11.172 6.062s8.188-1.715 10.854-5.143 4-7.934 4-13.52-1.355-10.135-4.063-13.648c-2.708-3.51-6.304-5.267-10.79-5.267zM271.136 187.447c0 13.29-2.486 23.307-7.46 30.057s-12.535 10.125-22.69 10.125c-9.988 0-17.51-3.292-22.566-9.872-5.058-6.58-7.65-16.323-7.776-29.23V172.53c0-13.287 2.485-23.252 7.458-29.896 4.973-6.643 12.558-9.966 22.757-9.966 10.112 0 17.655 3.237 22.63 9.712 4.97 6.475 7.52 16.166 7.647 29.072v15.995zm-15.425-17.265c0-8.674-1.185-15.033-3.554-19.075-2.37-4.04-6.137-6.062-11.3-6.062-5.035 0-8.738 1.915-11.107 5.745-2.37 3.83-3.62 9.807-3.746 17.932v20.948c0 8.633 1.206 15.064 3.618 19.297s6.2 6.348 11.362 6.348c4.95 0 8.61-1.957 10.98-5.87 2.37-3.915 3.62-10.04 3.746-18.378v-20.885z'/%3E%3C/g%3E%3C/svg%3E");
background-size: contain;
background-color: rgba(0, 0, 0, 0.5)
}
.video-js .vjs-big-vr-play-button .vjs-icon-placeholder {
display: none
}
:hover.video-js .vjs-big-vr-play-button {
-webkit-transition: border-color 0.4s, outline 0.4s, background-color 0.4s;
-moz-transition: border-color 0.4s, outline 0.4s, background-color 0.4s;
-ms-transition: border-color 0.4s, outline 0.4s, background-color 0.4s;
-o-transition: border-color 0.4s, outline 0.4s, background-color 0.4s;
transition: border-color 0.4s, outline 0.4s, background-color 0.4s
}
.video-js .vjs-big-vr-play-button::before {
content: ''
}
.video-js canvas {
cursor: move
}
.video-js .vjs-button-vr .vjs-icon-placeholder {
height: 30px;
width: 30px;
display: inline-block;
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZGRkZGRiI+CiAgICA8cGF0aCBkPSJNMjAuNzQgNkgzLjIxQzIuNTUgNiAyIDYuNTcgMiA3LjI4djEwLjQ0YzAgLjcuNTUgMS4yOCAxLjIzIDEuMjhoNC43OWMuNTIgMCAuOTYtLjMzIDEuMTQtLjc5bDEuNC0zLjQ4Yy4yMy0uNTkuNzktMS4wMSAxLjQ0LTEuMDFzMS4yMS40MiAxLjQ1IDEuMDFsMS4zOSAzLjQ4Yy4xOS40Ni42My43OSAxLjExLjc5aDQuNzljLjcxIDAgMS4yNi0uNTcgMS4yNi0xLjI4VjcuMjhjMC0uNy0uNTUtMS4yOC0xLjI2LTEuMjh6TTcuNSAxNC42MmMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTMgMS4xOCAwIDIuMTIuOTYgMi4xMiAyLjEzcy0uOTUgMi4xMi0yLjEyIDIuMTJ6bTkgMGMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTNzMi4xMi45NiAyLjEyIDIuMTMtLjk1IDIuMTItMi4xMiAyLjEyeiIvPgogICAgPHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyNHYyNEgwVjB6Ii8+Cjwvc3ZnPgo=) no-repeat left center
}

BIN
vr/src/assets/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

8
vr/src/assets/js/flv.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

56374
vr/src/assets/js/video.js Normal file

File diff suppressed because it is too large Load Diff

43971
vr/src/assets/js/videojs-vr.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,851 @@
<template>
<div id="videoContainer" class="vr-video">
<div class="video-wrapper">
<div id="video" @mousemove="ControlVisible()">
</div>
<div v-if="playVariables.statistics" class="vr-statistics">
<p>type{{playVariables.type || '???'}}</p>
<p>status{{playVariables.status || '???'}}</p>
<p>currentTime: {{playVariables.currentTime}}</p>
<p>totalTime: {{playVariables.totalTime}}</p>
<span v-if="deviceOrientationData.isSupported">
<p>
陀螺仪数据:
</p>
<p>
alpha(z轴):{{deviceOrientationData.alpha||'手机查看参数'}}
</p>
<p>
beta(x轴):{{deviceOrientationData.beta||'手机查看参数'}}
</p>
<p>
gamma(y轴):{{deviceOrientationData.gamma||'手机查看参数'}}
</p></span>
</div>
<div class="vr-func">
<div v-if="playVariables.status == 'pause'&&!playVariables.playClick"
@click="player.play();playVariables.playClick=true" class="btns-play">
<i class="iconfont icon-icon_play"></i>
</div>
<div v-else-if="playVariables.status == 'playing'&&!playVariables.playClick" @click="player.pause()"
class="btns-pause">
<i class="iconfont icon-ai07"></i>
</div>
</div>
<div v-if="playVariables.status == 'loading'" class="vr-loading">
<div class="border"></div>
<div class="slow"></div>
<div class="fast"></div>
</div>
<div class="vr-bar" v-if="playVariables.playClick" id="control">
<div class="bg"></div>
<div class="btns">
<div v-if="playVariables.status === 'playing'" @click="player.pause()" class="btns-pause">
<i class="iconfont icon-ai07"></i>
</div>
<div v-else @click="player.play()" class="btns-play">
<i class="iconfont icon-icon_play"></i>
</div>
</div>
<div v-if="playVariables.type == 'normal'" class="progress-container">
<div @click="jumpTo($event)" class="progress-wrapper">
<div class="progress" id="progress-play"></div>
</div>
<div class="btn-wrapper">
<div class="btn" id="progress-btn"></div>
</div>
</div>
<div v-else class="type">
<span class="statu-circle"></span>
<span>{{playVariables.type}}</span>
</div>
<div @click="changeFullscreenStatu()" class="fullscreen">
<i class="iconfont icon-quanping"></i>
</div>
</div>
<div class="vr-notice" id="vrNotice" v-html="playVariables.notice">
</div>
</div>
</div>
</template>
<script>
import * as THREE from 'three'
import threeOrbitControls from 'three-orbit-controls'
import flvjs from 'flv.js'
const OrbitControls = threeOrbitControls(THREE)
export default {
name: 'VRLive',
props: {
option: {
default: Object
}
},
data () {
return {
camera: null,
scene: null,
renderer: null,
mesh: null,
video: null,
controls: null,
deviceOrientationData: {},
hls: null,
player: {},
videoContainer: '',
playVariables: {
/*
视频类型默认Normal
Normal
HLS
FLV
*/
type: 'Normal',
/*
播放状态与视频播放状态对应
loading:加载中,
playing:视频播放中,包括视频中间加载后继续播放
pause:暂停或用户未点开始按钮
*/
status: 'pause',
playClick: false,
//
currentTime: 0,
progress: 0,
// fullscreenStatu: false,
notice: '',
statistics: false,
error: {
code: 0,
msg: ''
}
}
// cameraVariables: {
// isUserInteracting: false,
// lon: 0,
// lat: 0,
// phi: 0,
// theta: 0,
// distance: 1,
// onPointerDownPointerX: 0,
// onPointerDownPointerY: 0,
// onPointerDownLon: 0,
// onPointerDownLat: 0,
// sensitivity: 0.1
// }
}
},
// computed: {
// fullScreenStatu () {
// return document.mozFullScreen || document.webkitIsFullScreen
// }
// },
// watch: {
// playVariables (val) {
// },
// // fullScreenStatu (val) {
// // console.log(val)
// // if (val) {
// // this.videoContainer.classList.add('fullScreen-mobile')
// // } else {
// // this.videoContainer.classList.remove('fullScreen-mobile')
// // }
// // }
// },
mounted () {
if (this.check()) {
this.playVariables.statistics = this.option.statistics
this.playVariables.type = this.option.source.type
this.videoContainer = document.getElementById('videoContainer')
this.init()
}
//
if (!flvjs.isSupported()) {
console.error('Browser does not support flv.js')
return
}
//
const audioTest = document.createElement('audio')
if (!audioTest.canPlayType('audio/aac')) {
console.warn('Browser might not support AAC audio codec')
}
},
methods: {
check () {
if (!this.option.source) {
console.log(new Error('无播放源'))
} else if (!this.option.source.type) {
console.log(new Error('播放源type不能为空'))
} else if (!this.option.source.url) {
console.log(new Error('播放源url不能为空'))
} else {
return true
}
},
init () {
const container = document.getElementById('video')
this.initScene()
this.initCamera(container)
this.initRenderer(container)
this.initContent()
this.initControls(container)
this.render()
// this.addMouseEvent(container)
window.addEventListener('deviceorientation', function (event) {
this.deviceOrientationData.isSupported = true
this.deviceOrientationData = event
}.bind(this), false)
window.addEventListener('resize', function () {
this.onWindowResize(container)
}.bind(this))
},
initScene () {
this.scene = new THREE.Scene()
},
initCamera (el) {
this.camera = new THREE.PerspectiveCamera(75, el.clientWidth / el.clientHeight, 1, 1100)
this.camera.position.set(1, 0, 0)
// this.camera.target = new THREE.Vector3(0, 0, 0)
},
initRenderer (el) {
this.renderer = new THREE.WebGLRenderer()
this.renderer.setSize(el.offsetWidth, el.offsetHeight)
el.appendChild(this.renderer.domElement)
},
initVideo () {
this.video = document.createElement('video')
this.video.preload = 'auto'
this.video.muted = false
this.video.volume = 1.0
this.video.crossOrigin = 'anonymous'
// player video
this.player = this.video
this.video.addEventListener('waiting', function (event) {
this.playVariables.status = 'loading'
}.bind(this))
this.video.addEventListener('playing', function (event) {
this.playVariables.status = 'playing'
}.bind(this))
this.video.addEventListener('pause', function (event) {
this.playVariables.status = 'pause'
}.bind(this))
this.video.addEventListener('canplay', function (event) {
this.playVariables.duration = this.player.duration
if (this.playVariables.status === 'loading') {
this.playVariables.status = 'playing'
}
}.bind(this))
//
switch (this.option.source.type) {
case 'flv':
this.getFLV()
break
case 'hls':
this.getHLS(this.option.source.url, this.video)
break
case 'normal':
this.getNormalVideo(this.option.source.url, this.video)
break
default:
this.playVariables.error.code = 1
this.playVariables.error.msg = '未知的视频类型'
break
}
},
initContent () {
this.initVideo()
var geometry = new THREE.SphereBufferGeometry(300, 90, 90)
geometry.scale(-1, 1, 1)
var texture = new THREE.VideoTexture(this.video)
texture.minFilter = THREE.LinearFilter
texture.format = THREE.RGBFormat
var material = new THREE.MeshBasicMaterial({
map: texture
})
this.mesh = new THREE.Mesh(geometry, material)
this.mesh.position.set(0, 0, 0)
this.scene.add(this.mesh)
},
initControls (el) {
this.controls = new OrbitControls(this.camera, el)
// this.controls.target = new THREE.Vector3(0, Math.PI, 0)
this.controls.rotateSpeed = 0.05
this.controls.enableDamping = true
this.controls.dampingFactor = 0.05
},
render () {
requestAnimationFrame(this.render)
this.controls.update()
// this.cameraUpdate()
this.renderer.render(this.scene, this.camera)
},
getNormalVideo (sourceURL, el) {
const source = document.createElement('source')
source.src = sourceURL
// source.type = 'video/mp4'
el.appendChild(source)
this.player = el
},
getHLS (sourceURL, el) {
const Hls = require('hls.js')
if (Hls.isSupported()) {
this.hls = new Hls()
this.hls.loadSource(sourceURL)
this.hls.attachMedia(el)
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
console.log('加载成功')
})
this.hls.on(Hls.Events.ERROR, (event, data) => {
throw new Error(data.response.code + ' ' + data.response.text)
})
}
this.player = el
},
// abandoned rtmp
// video
// confuse object??
// getRTMP (sourceURL, el) {
// el.id = 'rtmpVideo'
// console.log(el)
// this.player = videojs(el, {
// sources: [{
// src: sourceURL
// }]
// })
// this.player.on('ready', function () {
// console.log('')
// })
// },
getFLV () {
if (flvjs.isSupported()) {
const flvPlayer = flvjs.createPlayer({
type: 'flv',
hasAudio: true,
hasVideo: true,
url: this.option.source.url
})
//
flvPlayer.on(flvjs.Events.ERROR, (errorType, errorDetail) => {
console.error('FLV Player Error:', errorType, errorDetail)
})
flvPlayer.on(flvjs.Events.MEDIA_INFO, (mediaInfo) => {
console.log('FLV Media Info:', mediaInfo)
})
flvPlayer.attachMediaElement(this.video)
flvPlayer.load()
return flvPlayer
}
},
onWindowResize (el) {
this.camera.aspect = el.clientWidth / el.clientHeight
this.camera.updateProjectionMatrix()
this.renderer.setSize(el.clientWidth, el.clientHeight)
},
// jumpTo ($e) {
// console.log(e)
// }
async changeFullscreenStatu () {
if (this.playVariables.fullscreenStatu) {
this.exitFullscreen()
// screen.orientation.unlock()
// this.videoContainer.classList.remove('full-screen-mobile')
} else {
this.fullScreen()
if (/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {
this.showNotice('为了更好的观看体验<br>请关闭屏幕锁定横屏观看该视频')
}
//
// screen.orientation.lock('landscape-primary')
// this.videoContainer.classList.add('full-screen-mobile')
// screen.lockOrientationUniversal = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation
// new ScreenOrientation().lock('landscape-secondary')
// console.log(screen)
// screen.msLockOrientation.lock('landscape-primary')
}
this.playVariables.fullscreenStatu = !this.playVariables.fullscreenStatu
},
fullScreen () {
const el = this.videoContainer
if (el.requestFullscreen) {
el.requestFullscreen()
} else if (el.mozRequestFullScreen) {
el.mozRequestFullScreen()
} else if (el.webkitRequestFullScreen) {
el.webkitRequestFullScreen()
}
},
exitFullscreen () {
const el = document
el.exitFullscreen()
if (el.exitFullscreen) {
el.exitFullscreen()
} else if (el.mozCancelFullScreen) {
el.mozCancelFullScreen()
} else if (el.webkitCancelFullScreen) {
el.webkitCancelFullScreen()
}
},
showNotice (msg) {
this.playVariables.notice = msg
const notice = document.getElementById('vrNotice')
if (notice.classList.contains('vr-notice-show')) {
notice.classList.remove('vr-notice-show')
setTimeout(() => {
notice.classList.add('vr-notice-show')
}, 0)
} else {
notice.classList.add('vr-notice-show')
}
},
ControlVisible () {
const control = document.getElementById('control')
if (this.playVariables.status === 'playing') {
if (control.classList.contains('control-hidden')) {
control.classList.remove('control-hidden')
setTimeout(() => {
control.classList.add('control-hidden')
}, 0)
} else {
control.classList.add('control-hidden')
}
}
}
// three
// abandoned ,使
// addMouseEvent (el) {
// el.addEventListener('mousedown', onDocumentMouseDown.bind(this), false)
// el.addEventListener('mousemove', onDocumentMouseMove.bind(this), false)
// el.addEventListener('mouseup', onDocumentMouseUp.bind(this), false)
// el.addEventListener('touchstart', onDocumentTouchStart.bind(this), false)
// el.addEventListener('touchmove', onDocumentTouchMove.bind(this), false)
// el.addEventListener('touchend', onDocumentTouchEnd.bind(this), false)
// function onDocumentMouseDown (event) {
// event.preventDefault()
// this.cameraVariables.isUserInteracting = true
// this.cameraVariables.onPointerDownPointerX = event.clientX
// this.cameraVariables.onPointerDownPointerY = event.clientY
// this.cameraVariables.onPointerDownLon = this.cameraVariables.lon
// this.cameraVariables.onPointerDownLat = this.cameraVariables.lat
// }
// function onDocumentMouseMove (event) {
// if (this.cameraVariables.isUserInteracting === true) {
// this.cameraVariables.lon =
// (this.cameraVariables.onPointerDownPointerX - event.clientX) * this.cameraVariables.sensitivity +
// this.cameraVariables.onPointerDownLon
// this.cameraVariables.lat =
// (event.clientY - this.cameraVariables.onPointerDownPointerY) * this.cameraVariables.sensitivity +
// this.cameraVariables.onPointerDownLat
// console.log(this.cameraVariables.lat)
// }
// }
// function onDocumentMouseUp () {
// this.cameraVariables.isUserInteracting = false
// }
// function onDocumentTouchStart (event) {
// event.preventDefault()
// this.cameraVariables.isUserInteracting = true
// this.cameraVariables.onPointerDownPointerX = event.touches[0].clientX
// this.cameraVariables.onPointerDownPointerY = event.touches[0].clientY
// this.cameraVariables.onPointerDownLon = this.cameraVariables.lon
// this.cameraVariables.onPointerDownLat = this.cameraVariables.lat
// }
// function onDocumentTouchMove (event) {
// if (this.cameraVariables.isUserInteracting === true) {
// this.cameraVariables.lon =
// (this.cameraVariables.onPointerDownPointerX - event.touches[0].clientX) * this.cameraVariables
// .sensitivity +
// this.cameraVariables.onPointerDownLon
// this.cameraVariables.lat =
// (event.touches[0].clientY - this.cameraVariables.onPointerDownPointerY) * this.cameraVariables
// .sensitivity +
// this.cameraVariables.onPointerDownLat
// }
// }
// function onDocumentTouchEnd () {
// this.cameraVariables.isUserInteracting = false
// }
// // const pre = {
// // x: '',
// // y: ''
// // }
// // // const cur = {
// // // x: '',
// // // y: ''
// // // }
// // let isDown = false
// // const self = this
// // el.onmousedown = function (event) {
// // isDown = true
// // }
// // el.onmouseup = function (event) {
// // isDown = false
// // }
// // el.onmousemove = function (event) {
// // if (isDown) {
// // console.log(event)
// // console.log(pre)
// // console.log(self.camera)
// // if (event.movementY && !event.movementX) {
// // self.camera.rotation.x -= event.movementY / 500
// // } else if (!event.movementY && event.movementX) {
// // self.camera.rotation.y += event.movementX / 500
// // } else {
// // pre.x = event.clientX
// // }
// // pre.y = event.clientY
// // }
// // }
// },
// cameraUpdate () {
// this.cameraVariables.lat = Math.max(-85, Math.min(85, this.cameraVariables.lat))
// this.cameraVariables.phi = THREE.Math.degToRad(90 - this.cameraVariables.lat)
// this.cameraVariables.theta = THREE.Math.degToRad(this.cameraVariables.lon)
// this.camera.position.x = this.cameraVariables.distance * Math.sin(this.cameraVariables.phi) * Math.cos(this.cameraVariables.theta)
// this.camera.position.y = this.cameraVariables.distance * Math.cos(this.cameraVariables.phi)
// this.camera.position.z = this.cameraVariables.distance * Math.sin(this.cameraVariables.phi) * Math.sin(this.cameraVariables.theta)
// this.camera.lookAt(this.camera.target)
// //
// setTimeout(() => {
// }, 1000)
// }
}
}
</script>
<style lang="scss" scoped>
.vr-video {
position: relative;
width: 100%;
height: 100%;
.video-wrapper {
display: flex;
align-items: center;
height: 100%;
// min-height: 300px;
}
#video {
width: 100%;
height: 100%;
&:hover {
.control {
display: block;
animation: mousehover 3s;
animation-delay: 5s;
animation-fill-mode: forwards;
}
}
}
.control-hidden {
visibility: visible;
opacity: 1;
animation: mousehover 2s;
animation-delay: 3s;
animation-fill-mode: forwards;
}
@keyframes mousehover {
100% {
visibility: hidden;
opacity: 0;
}
}
.vr {
&-func {
position: absolute;
z-index: 2;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 50px;
font-size: 16px;
.btns-play,
.btns-pause {
i {
font-size: 4em;
color: rgba(255, 255, 255, 1);
transition: color .3s;
}
&:hover {
cursor: pointer;
i {
color: rgba(255, 255, 255, 0.6);
}
}
}
}
&-loading {
position: absolute;
z-index: 2;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
width: 3em;
height: 3em;
.border {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0.45em solid rgba(99, 149, 168, 0.5);
border-radius: 50%;
}
.slow {
position: absolute;
z-index: 2;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-width: 0.45em;
border-style: solid;
border: 0.45em solid rgba(0, 0, 0, 0);
border-top-color: rgb(7, 186, 241);
animation: slow 1.5s linear infinite;
border-radius: 50%;
}
.fast {
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-width: 0.45em;
border-style: solid;
border: 0.45em solid rgba(0, 0, 0, 0);
border-top-color: rgb(10, 126, 161);
animation: fast 0.75s linear infinite;
border-radius: 50%;
}
@keyframes slow {
to {
transform: rotate(360deg);
}
}
@keyframes fast {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(45deg);
}
50% {
border-top-color: rgb(195, 236, 248);
}
75% {
transform: rotate(315deg);
}
100% {
transform: rotate(360deg);
}
}
}
&-bar {
position: absolute;
z-index: 10;
bottom: 0;
display: flex;
align-items: center;
padding: 1em 1em;
box-sizing: border-box;
width: 100%;
background-image: linear-gradient(to top, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
.btns {
padding: 0.5em;
&-play,
&-pause {
i {
color: white;
}
}
&:hover {
cursor: pointer;
}
}
.progress-container {
position: relative;
flex-grow: 1;
display: flex;
align-items: center;
padding: 0 1em;
.progress-wrapper {
position: relative;
width: 100%;
height: 0.2em;
border-radius: 30px;
background-color: rgba(150, 150, 150, 1);
overflow: hidden;
.progress {
position: absolute;
width: 0%;
height: 100%;
background-color: rgb(22, 175, 236);
transition: width .5s;
}
&:hover {
cursor: pointer;
}
}
.btn-wrapper {
position: absolute;
z-index: 9;
left: 1em;
width: calc(100% - 2em);
.btn {
margin-left: 0;
width: 0.8em;
height: 0.8em;
border-radius: 50%;
background-color: white;
}
}
}
.type {
padding: 0 2em;
width: 100%;
font-size: 0.95em;
font-weight: 300;
color: white;
.statu-circle {}
}
.fullscreen {
i {
font-size: 1.4em;
color: white;
}
&:hover {
cursor: pointer;
}
}
}
&-notice {
position: absolute;
z-index: 99;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
visibility: hidden;
opacity: 0;
padding: 0.5em 1em;
white-space: nowrap;
font-size: 1em;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 0.5em;
&-show {
visibility: visible;
opacity: 1;
animation: notice 2s;
animation-delay: 3s;
animation-fill-mode: forwards;
}
@keyframes notice {
100% {
visibility: hidden;
opacity: 0;
}
}
}
&-statistics {
position: absolute;
z-index: 1;
top: 1em;
left: 1em;
padding: 0.2em 1em;
font-size: 13px;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 0.2em;
}
}
&:fullscreen {
.video-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
// transform-origin: top left;
// transform: rotate(90deg) translate(-0vh, -100vw);
}
}
}
// .full-screen-mobile {
// .video-wrapper {
// position: absolute;
// z-index: 99;
// top: 0;
// left: 0;
// width: 100vh;
// height: 100vw;
// transform-origin: top left;
// transform: rotate(90deg) translate(-0vh, -100vw);
// }
// // #video {
// // width: 100%;
// // height: calc(100vw / 16 * 9);
// // }
// }
</style>

10
vr/src/main.js Normal file
View File

@ -0,0 +1,10 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')

36
vr/src/router/index.js Normal file
View File

@ -0,0 +1,36 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/vrlive'
},
{
path: '/home',
name: 'Home',
component: () => import('../views/Home.vue')
}, {
path: '/live',
name: 'Live',
component: () => import('../views/Live')
}, {
path: '/vrlive',
name: 'VRLive',
component: () => import('../views/VRLive')
}, {
path: '/test',
name: 'Test',
component: () => import('../views/Test')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router

15
vr/src/store/index.js Normal file
View File

@ -0,0 +1,15 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})

0
vr/src/views/Home.vue Normal file
View File

View File

@ -0,0 +1,52 @@
<template>
<div class="live">
<video class="video-js my-video" ref="video"></video>
</div>
</template>
<script>
import videojs from 'video.js'
import 'videojs-flash'
export default {
name: 'Live',
data () {
return {
player: null,
opt: {
techOrder: ['flash', 'html5'],
sources: [{
withCredentials: false,
type: 'rtmp/flv',
// rtmp://202.69.69.180:443/webcast/bshdlive-pc
// rtmp://live.xshaitt.com/kxh/demo
src: 'rtmp://localhost:8000/live/livestream'
}, {
withCredentials: false,
type: 'application/x-mpegURL',
// http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8
// http://live.xshaitt.com/kxh/demo.m3u8
src: 'http://localhost:8079/live/livestream.flv'
}],
controls: true,
poster: 'https://images.pexels.com/photos/3518091/pexels-photo-3518091.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500'
}
}
},
mounted () {
console.log(1, this.$refs.video)
this.player = videojs(this.$refs.video, this.opt)
}
}
</script>
<style lang="scss" scoped>
.live{
position: relative;
width: 100%;
.video-js{
width: 100%;
height: 400px;
}
}
</style>

100
vr/src/views/Test/index.vue Normal file
View File

@ -0,0 +1,100 @@
<template>
<div class="vrlive">
<video class="video-js my-video" ref="video"></video>
<div class="func">
<button @click="player.play()">播放</button>
<button @click="player.pause()">暂停</button>
</div>
123
</div>
</template>
<script>
import videojs from 'video.js'
import 'videojs-flash'
import '@a/js/videojs-vr'
export default {
name: 'VRLive',
data () {
return {
player: null,
opt: {
techOrder: ['flash', 'html5'],
sources: [{
withCredentials: false,
type: 'rtmp/flv',
// rtmp://202.69.69.180:443/webcast/bshdlive-pc
// rtmp://live.xshaitt.com/kxh/demo
src: 'rtmp://202.69.69.180:443/webcast/bshdlive-pc'
}, {
withCredentials: false,
type: 'application/x-mpegURL',
// http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8
// http://live.xshaitt.com/kxh/demo.m3u8
src: ' http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8'
}],
controls: true,
poster: 'https://images.pexels.com/photos/3518091/pexels-photo-3518091.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500'
}
}
},
mounted () {
this.init()
},
methods: {
init () {
this.player = videojs(this.$refs.video, this.opt)
this.player.vr({ projection: '360' })
},
getRTMP (sourceURL, el) {
el.id = 'rtmpVideo'
console.log(el)
this.player = videojs(el, {
height: '432',
sources: [{
type: 'rtmp/flv',
src: sourceURL
}]
})
this.player.on('ready', function () {
console.log('准备就绪')
})
}
}
}
</script>
<style lang="scss" scoped>
.vrlive{
display: flex;
flex-direction: column;
align-items: center;
.my-video{
margin-top: 40px;
width: 800px;
height: 600px;
}
.func{
display: flex;
justify-content: center;
margin-top: 20px;
width: 800px;
button{
margin-right: 20px;
padding: 10px 15px;
font-size: 15px;
color: white;
background-image: linear-gradient( 135deg, #ABDCFF 10%, #0396FF 100%);
border-radius: 5px;
transition: all .3s;
&:hover{
transform: translateY(-2px);
background-image: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
box-shadow: 0 2px 5px #40bad5;
}
}
}
}
</style>

69
vr/src/views/VRLive.vue Normal file
View File

@ -0,0 +1,69 @@
<template>
<div class="vr-live">
<p>{{opt.source.src}}</p>
<div class="container">
<vr-live :option="opt"></vr-live>
</div>
</div>
</template>
<script>
import vrLive from '@c/VRLive'
export default {
components: {
vrLive
},
data () {
return {
opt: {
statistics: false, //
source: {
/*
视频类型
normal
hls
flv
*/
type: 'flv',
/*
测试地址
https://www.wangwentehappy.tk/assets/video/1.mp4
http://playertest.longtailvideo.com/adaptive/bipbop/gear4/prog_index.m3u8
http://localhost:9001/live/wwt.flv
http://localhost:9001/live/wwt/index.m3u8
*/
// url: 'https://www.ashenone.tk/public/video/1.mp4'
url: 'http://localhost:8079/live/livestream.flv'
}
}
}
}
}
</script>
<style lang="scss" scoped>
.vr-live{
display: inline-block;
width: 100%;
height: 100%;
.container{
margin: 80px auto 0 auto;
padding: 15px;
width: 70vw;
height: calc(70vw / 16 * 9);
background: rgba(125,125,125,1);
border-radius: 10px;
}
@media screen and (max-width: 768px) {
.container{
margin: 0;
padding: 0;
width: 100vw;
height: calc(100vw / 16 * 9);
background: none;
border-radius: 0;
}
}
}
</style>

12
vr/tests/e2e/.eslintrc.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
plugins: [
'cypress'
],
env: {
mocha: true,
'cypress/globals': true
},
rules: {
strict: 'off'
}
}

View File

@ -0,0 +1,25 @@
/* eslint-disable arrow-body-style */
// https://docs.cypress.io/guides/guides/plugins-guide.html
// if you need a custom webpack configuration you can uncomment the following import
// and then use the `file:preprocessor` event
// as explained in the cypress docs
// https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples
// /* eslint-disable import/no-extraneous-dependencies, global-require */
// const webpack = require('@cypress/webpack-preprocessor')
module.exports = (on, config) => {
// on('file:preprocessor', webpack({
// webpackOptions: require('@vue/cli-service/webpack.config'),
// watchOptions: {}
// }))
return Object.assign({}, config, {
fixturesFolder: 'tests/e2e/fixtures',
integrationFolder: 'tests/e2e/specs',
screenshotsFolder: 'tests/e2e/screenshots',
videosFolder: 'tests/e2e/videos',
supportFile: 'tests/e2e/support/index.js'
})
}

View File

@ -0,0 +1,8 @@
// https://docs.cypress.io/api/introduction/api.html
describe('My First Test', () => {
it('Visits the app root url', () => {
cy.visit('/')
cy.contains('h1', 'Welcome to Your Vue.js App')
})
})

View File

@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })

View File

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@ -0,0 +1,12 @@
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'
describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
const wrapper = shallowMount(HelloWorld, {
propsData: { msg }
})
expect(wrapper.text()).toMatch(msg)
})
})

46
vr/vue.config.js Normal file
View File

@ -0,0 +1,46 @@
const path = require('path')
function resolve (dir) {
return path.join(__dirname, dir)
}
module.exports = {
lintOnSave: false,
devServer: {
open: true,
openPage: '',
host: '0.0.0.0',
port: 8080,
https: false,
proxy: null, // 设置代理
before: app => {}
},
css: {
loaderOptions: {
sass: {
// 添加这个配置来支持 @import
sassOptions: {
quietDeps: true
},
// 使用新的 API
implementation: require('sass')
}
}
},
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src'))
.set('@a', resolve('src/assets'))
.set('@c', resolve('src/components'))
// 优化大文件处理
config.performance
.maxEntrypointSize(1024000)
.maxAssetSize(1024000)
},
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all',
maxSize: 250000
}
}
}
}