Showing preview only (299K chars total). Download the full file or copy to clipboard to get everything.
Repository: nicepkg/vr360
Branch: master
Commit: 7d6598b1134d
Files: 193
Total size: 241.0 KB
Directory structure:
gitextract_4oa5dsxh/
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── commit-convention.md
│ ├── settings.yml
│ └── workflows/
│ ├── docs.yml
│ ├── lint.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .husky/
│ ├── commit-msg
│ ├── pre-commit
│ └── prepare-commit-msg
├── .npmrc
├── .stylelintignore
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── commitlint.config.js
├── lint-staged.config.js
├── netlify.toml
├── package.json
├── packages/
│ ├── doc/
│ │ ├── .vuepress/
│ │ │ ├── client.ts
│ │ │ ├── components/
│ │ │ │ ├── DemoA.vue
│ │ │ │ └── NpmBadge.vue
│ │ │ ├── configs/
│ │ │ │ ├── index.ts
│ │ │ │ ├── navbar/
│ │ │ │ │ ├── en.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── zh.ts
│ │ │ │ └── sidebar/
│ │ │ │ ├── en.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── zh.ts
│ │ │ ├── examples/
│ │ │ │ └── firstHouse/
│ │ │ │ ├── demo.css
│ │ │ │ ├── html.html
│ │ │ │ ├── react.tsx
│ │ │ │ ├── vue2.vue
│ │ │ │ └── vue3.vue
│ │ │ ├── index.build.html
│ │ │ ├── plugins/
│ │ │ │ ├── code-demo/
│ │ │ │ │ ├── CodeDemo.props.ts
│ │ │ │ │ ├── CodeDemo.vue
│ │ │ │ │ ├── Icons.tsx
│ │ │ │ │ ├── clientConfigFile.ts
│ │ │ │ │ ├── constant.ts
│ │ │ │ │ ├── global.d.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── plugin.ts
│ │ │ │ └── index.ts
│ │ │ ├── public/
│ │ │ │ ├── browserconfig.xml
│ │ │ │ ├── code-demo-templates/
│ │ │ │ │ ├── demo.css.txt
│ │ │ │ │ ├── html/
│ │ │ │ │ │ └── package.json.txt
│ │ │ │ │ ├── react/
│ │ │ │ │ │ ├── index.html.txt
│ │ │ │ │ │ ├── package.json.txt
│ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ └── main.tsx.txt
│ │ │ │ │ │ ├── tsconfig.json.txt
│ │ │ │ │ │ └── vite.config.ts.txt
│ │ │ │ │ ├── vue2/
│ │ │ │ │ │ ├── index.html.txt
│ │ │ │ │ │ ├── package.json.txt
│ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ ├── App.vue.txt
│ │ │ │ │ │ │ └── main.ts.txt
│ │ │ │ │ │ ├── tsconfig.json.txt
│ │ │ │ │ │ ├── types/
│ │ │ │ │ │ │ └── module.d.ts.txt
│ │ │ │ │ │ └── vite.config.ts.txt
│ │ │ │ │ └── vue3/
│ │ │ │ │ ├── index.html.txt
│ │ │ │ │ ├── package.json.txt
│ │ │ │ │ ├── src/
│ │ │ │ │ │ ├── App.vue.txt
│ │ │ │ │ │ └── main.ts.txt
│ │ │ │ │ ├── tsconfig.json.txt
│ │ │ │ │ ├── types/
│ │ │ │ │ │ └── module.d.ts.txt
│ │ │ │ │ └── vite.config.ts.txt
│ │ │ │ └── manifest.webmanifest
│ │ │ ├── styles/
│ │ │ │ └── index.scss
│ │ │ ├── theme/
│ │ │ │ ├── components/
│ │ │ │ │ ├── Home.vue
│ │ │ │ │ ├── HomeFeatures.vue
│ │ │ │ │ └── HomeVrBg.vue
│ │ │ │ └── index.ts
│ │ │ ├── types/
│ │ │ │ └── module.d.ts
│ │ │ └── utils/
│ │ │ └── common.ts
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── bundler.config.ts
│ │ ├── guide/
│ │ │ ├── README.md
│ │ │ └── questions.md
│ │ ├── libs/
│ │ │ ├── vr360-core/
│ │ │ │ ├── README.md
│ │ │ │ ├── events.md
│ │ │ │ ├── example.md
│ │ │ │ ├── methods.md
│ │ │ │ └── properties.md
│ │ │ ├── vr360-ui/
│ │ │ │ └── README.md
│ │ │ ├── vr360-ui-react/
│ │ │ │ └── README.md
│ │ │ ├── vr360-ui-vue2/
│ │ │ │ └── README.md
│ │ │ └── vr360-ui-vue3/
│ │ │ └── README.md
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── vuepress.config.ts
│ ├── vr360-core/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── scripts/
│ │ │ └── build.ts
│ │ ├── src/
│ │ │ ├── helper.ts
│ │ │ ├── index.ts
│ │ │ ├── manager/
│ │ │ │ ├── index.ts
│ │ │ │ ├── space.ts
│ │ │ │ └── tip.ts
│ │ │ ├── types.ts
│ │ │ └── vr360.ts
│ │ ├── test/
│ │ │ ├── index.ts
│ │ │ └── setup.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── vr360-shared/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── build.config.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── build-utils/
│ │ │ │ ├── build-script.util.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── vite-config-common.util.ts
│ │ │ ├── index.ts
│ │ │ ├── test-react-utils.ts
│ │ │ ├── test-utils/
│ │ │ │ ├── common/
│ │ │ │ │ ├── helper.util.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── mock-global-api.ts
│ │ │ │ │ ├── mock-server.util.ts
│ │ │ │ │ ├── polyfill-fetch.util.ts
│ │ │ │ │ └── polyfill-pointer-events.util.ts
│ │ │ │ ├── react/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── react-helper.util.ts
│ │ │ │ │ └── react-mount.util.ts
│ │ │ │ └── vue/
│ │ │ │ ├── index.ts
│ │ │ │ ├── vue-helper.util.ts
│ │ │ │ └── vue-mount.util.ts
│ │ │ ├── test-utils.ts
│ │ │ └── test-vue-utils.ts
│ │ ├── test-react-utils.d.ts
│ │ ├── test-utils.d.ts
│ │ ├── test-vue-utils.d.ts
│ │ ├── tsconfig.json
│ │ └── types/
│ │ └── global.d.ts
│ ├── vr360-ui/
│ │ └── README.md
│ ├── vr360-ui-react/
│ │ └── README.md
│ ├── vr360-ui-vue2/
│ │ └── README.md
│ └── vr360-ui-vue3/
│ └── README.md
├── playgrounds/
│ ├── react/
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── Example.tsx
│ │ │ └── main.tsx
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── vue2/
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.vue
│ │ │ └── main.ts
│ │ ├── tsconfig.json
│ │ ├── types/
│ │ │ └── module.d.ts
│ │ ├── unocss.config.ts
│ │ └── vite.config.ts
│ └── vue3/
│ ├── index.html
│ ├── package.json
│ ├── src/
│ │ ├── App.vue
│ │ ├── ContextMenu.vue
│ │ ├── Editor.vue
│ │ ├── EditorHotPointManager.vue
│ │ ├── EditorLeftBar.vue
│ │ ├── EditorSceneManager.vue
│ │ ├── EditorSettings.vue
│ │ ├── EditorTipsManager.vue
│ │ ├── EditorTopBar.vue
│ │ ├── Icons.tsx
│ │ ├── helper.ts
│ │ ├── main.ts
│ │ └── useVr360.ts
│ ├── tsconfig.json
│ ├── types/
│ │ └── module.d.ts
│ ├── unocss.config.ts
│ └── vite.config.ts
├── pnpm-workspace.yaml
├── prettier.config.js
├── scripts/
│ ├── build.ts
│ ├── check-update.ts
│ ├── release.ts
│ └── utils.ts
├── stylelint.config.js
├── textures.json
├── tsconfig-base.json
├── tsconfig.json
└── turbo.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
================================================
FILE: .eslintignore
================================================
**/node_modules/
*.html
es/
**/lib/
**/dist/
**/lib-types/
_site/
**/dist/
CHANGELOG.md
.rollup.cache
tsconfig.tsbuildinfo
!.storybook
!.vuepress
types/components.d.ts
types/auto-imports.d.ts
*.generated.ts
**/.vuepress/examples/
================================================
FILE: .eslintrc.js
================================================
//@ts-check
module.exports = /** @type { import('eslint').Linter.Config } */ ({
root: true,
extends: [
'eslint:recommended',
'plugin:eslint-comments/recommended',
'plugin:import/recommended',
'plugin:unicorn/recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:prettier/recommended',
'prettier'
],
env: {
node: true,
browser: true,
es2022: true
},
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
settings: {
react: {
version: 'detect'
},
'import/extensions': ['.js', '.jsx']
},
ignorePatterns: ['node_modules/*'],
rules: {
/**
* Turn-off recommended rules
*/
'array-callback-return': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/no-autofocus': 'off',
'react/display-name': 'off',
'react/prop-types': 'off',
'eslint-comments/disable-enable-pair': 'off',
'unicorn/no-array-reduce': 'off',
'unicorn/filename-case': 'off',
'unicorn/no-null': 'off',
'unicorn/prevent-abbreviations': 'off',
'unicorn/prefer-module': 'off',
'unicorn/prefer-top-level-await': 'off',
'unicorn/consistent-function-scoping': 'off',
'unicorn/no-array-for-each': 'off',
'unicorn/prefer-spread': 'off',
/**
* Adjust recommended rules
*/
'no-empty': ['error', {allowEmptyCatch: true}],
'no-unused-vars': ['error', {args: 'none', ignoreRestSiblings: true}],
'react-hooks/exhaustive-deps': ['error', {additionalHooks: '(useRecoilCallback|useRecoilTransaction)'}],
/**
* Use additional rules
*/
'default-case': 'error',
eqeqeq: ['error', 'smart'],
'no-array-constructor': 'error',
'no-caller': 'error',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-extra-label': 'error',
'no-implied-eval': 'error',
'no-label-var': 'error',
'no-labels': 'error',
'no-lone-blocks': 'error',
'no-loop-func': 'error',
'no-multi-str': 'error',
'no-new-func': 'error',
'no-new-object': 'error',
'no-new-wrappers': 'error',
'no-restricted-globals': ['error', ...require('confusing-browser-globals')],
'no-script-url': 'error',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-template-curly-in-string': 'error',
'no-throw-literal': 'error',
'no-unused-expressions': [
'error',
{
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true
}
],
'no-useless-computed-key': 'error',
'no-useless-concat': 'error',
'no-useless-constructor': 'error',
'no-useless-rename': 'error',
strict: ['error', 'never'],
'react/jsx-pascal-case': ['error', {allowAllCaps: true}],
'react/no-array-index-key': 'error',
'react/no-typos': 'error',
'react/style-prop-object': 'error'
},
overrides: [
{
files: ['**/__tests__/**/*', '**/*.{spec,test}.*'],
extends: ['plugin:testing-library/react', 'plugin:jest-dom/recommended']
},
{
files: ['**/*.stories.*'],
rules: {
'import/no-anonymous-default-export': 'off'
}
},
{
files: ['**/*.ts?(x)', '**/*.vue'],
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaversion: 2022,
sourceType: 'module',
ecmaFeatures: {
jsx: true
},
extraFileExtensions: ['.vue'],
project: [
'./tsconfig.json',
'./packages/*/tsconfig.json',
'./examples/**/tsconfig.json',
'./playgrounds/**/tsconfig.json'
]
},
settings: {
react: {version: 'detect'},
'import/resolver': {
typescript: {
alwaysTryTypes: true
}
}
},
env: {
browser: true,
node: true,
es6: true
},
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'plugin:import/typescript',
'plugin:vue/recommended',
'plugin:prettier/recommended',
'prettier'
],
rules: {
/**
* Turn-off recommended rules
*/
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
'@typescript-eslint/prefer-ts-expect-error': 'off',
// 'tsc' already handles this (https://typescript-eslint.io/docs/linting/troubleshooting/#eslint-plugin-import)
'import/default': 'off',
'import/namespace': 'off',
'import/no-named-as-default-member': 'off',
'import/no-unresolved': 'off',
/**
* Adjust recommended rules
*/
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/no-misused-promises': ['error', {checksVoidReturn: {arguments: false, attributes: false}}],
'@typescript-eslint/no-unused-vars': ['error', {args: 'none', ignoreRestSiblings: true}],
/**
* Use additional rules
*/
'@typescript-eslint/consistent-type-imports': 'error',
'import/first': 'error',
'import/no-anonymous-default-export': 'error',
/**
* Replace additional rules
*/
'default-case': 'off', // 'tsc' noFallthroughCasesInSwitch option is more robust
'no-array-constructor': 'off',
'@typescript-eslint/no-array-constructor': 'error',
'no-implied-eval': 'off',
'@typescript-eslint/no-implied-eval': 'error',
'no-loop-func': 'off',
'@typescript-eslint/no-loop-func': 'error',
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': [
'error',
{
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true
}
],
'no-throw-literal': 'off',
'@typescript-eslint/no-throw-literal': 'error',
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'error',
// vue
'vue/no-v-html': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/html-self-closing': 'off',
'vue/max-attributes-per-line': [
'error',
{
singleline: 10,
multiline: 1
}
],
'vue/require-default-prop': 'off',
'vue/html-closing-bracket-spacing': 'error',
'vue/no-unused-vars': 'warn',
'vue/multi-word-component-names': 'off',
'vue/one-component-per-file': 'off',
'vue/no-v-model-argument': 'off',
'vue/comment-directive': [
'warn',
{
reportUnusedDisableDirectives: false
}
]
}
}
]
})
================================================
FILE: .github/FUNDING.yml
================================================
github: [2214962083]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: "\U0001F41E Bug report"
description: Report an issue with Vr360
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
placeholder: Bug description
validations:
required: true
- type: input
id: reproduction
attributes:
label: Reproduction
description: Please provide a link via [vr360](https://stackblitz.com/edit/vr360/) or a link to a repo that can reproduce the problem you ran into. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required.
placeholder: Reproduction
validations:
required: true
- type: textarea
id: system-info
attributes:
label: System Info
description: Output of `npx envinfo --system --npmPackages '{@nicepkg/vr360-core,three}' --binaries --browsers`
render: Shell
placeholder: System, Binaries, Browsers
validations:
required: true
- type: dropdown
id: package-manager
attributes:
label: Used Package Manager
description: Select the used package manager
options:
- npm
- yarn
- pnpm
validations:
required: true
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: Follow our [Code of Conduct](https://github.com/nicepkg/vr360/blob/master/CODE_OF_CONDUCT.md)
required: true
- label: Read the [Contributing Guidelines](https://github.com/nicepkg/vr360/blob/master/CONTRIBUTING.md).
required: true
- label: Check that there isn't [already an issue](https://github.com/nicepkg/vr360/issues) that reports the same bug to avoid creating a duplicate.
required: true
- label: Make sure this is a Vr360 issue and not a framework-specific issue. For example, if it's a threejs related bug, it should likely be reported to https://github.com/mrdoob/three.js instead.
required: true
- label: Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/nicepkg/vr360/discussions).
required: true
- label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Question
url: https://github.com/nicepkg/vr360/discussions/new?category=Q-A
about: Ask a question or discuss about vr360
- name: Ideas
url: https://github.com/nicepkg/vr360/discussions/new?category=Ideas
about: Start a discussion to improve vr360
- name: GitHub Sponsors
url: https://github.com/sponsors/2214962083
about: Like this project? Please consider supporting the author.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: "\U0001F680 New feature proposal"
description: Propose a new feature to be added to Vr360
labels: ['feature request']
body:
- type: markdown
attributes:
value: |
Thanks for your interest in the project and taking the time to fill out this feature report!
- type: textarea
id: feature-description
attributes:
label: Clear and concise description of the problem
description: 'As a developer using xxxx package I want [goal / wish] so that [benefit]. If you intend to submit a PR for this issue, tell us in the description. Thanks!'
validations:
required: true
- type: textarea
id: suggested-solution
attributes:
label: Suggested solution
description: 'In module [xy] we could provide following implementation...'
validations:
required: true
- type: textarea
id: alternative
attributes:
label: Alternative
description: Clear and concise description of any alternative solutions or features you've considered.
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Any other context or screenshots about the feature request here.
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: Follow our [Code of Conduct](https://github.com/nicepkg/vr360/blob/master/CODE_OF_CONDUCT.md)
required: true
- label: Read the [Contributing Guidelines](https://github.com/nicepkg/vr360/blob/master/CONTRIBUTING.md).
required: true
- label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
required: true
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!-- Thank you for contributing! -->
### Description
<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->
### Additional context
<!-- e.g. is there anything you'd like reviewers to focus on? -->
---
### What is the purpose of this pull request? <!-- (put an "X" next to an item) -->
- [ ] Bug fix
- [ ] New Feature
- [ ] Documentation update
- [ ] Other
### Before submitting the PR, please make sure you do the following
- [ ] Read the [Contributing Guidelines](https://github.com/nicepkg/vr360/blob/master/CONTRIBUTING.md).
- [ ] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
- [ ] Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `fixes #123`).
- [ ] Ideally, include relevant tests that fail without this PR but pass with it.
================================================
FILE: .github/commit-convention.md
================================================
## Git Commit Message Convention
> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
#### TL;DR:
Messages must be matched by the following regex:
```text
/^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip)(\(.+\))?: .{1,50}/
```
#### Examples
Appears under "Features" header, `link` subheader:
```
feat(link): add `force` option
```
Appears under "Bug Fixes" header, `view` subheader, with a link to issue #28:
```
fix(view): handle keep-alive with aborted navigations
close #28
```
Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
```
perf: improve guard extraction
BREAKING CHANGE: The 'beforeRouteEnter' option has been removed.
```
The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
```
revert: feat(compiler): add 'comments' option
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
```
### Full Message Format
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
The **header** is mandatory and the **scope** of the header is optional.
### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
### Type
If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.
Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.
### Scope
The scope could be anything specifying the place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc...
### Subject
The subject contains a succinct description of the change:
- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize the first letter
- no dot (.) at the end
### Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.
### Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
================================================
FILE: .github/settings.yml
================================================
labels:
- name: bug
color: ee0701
- name: contribution welcome
color: 0e8a16
- name: discussion
color: 4935ad
- name: docs
color: 8be281
- name: enhancement
color: a2eeef
- name: good first issue
color: 7057ff
- name: help wanted
color: 008672
- name: question
color: d876e3
- name: wontfix
color: ffffff
- name: WIP
color: ffffff
- name: need repro
color: c9581c
- name: feature request
color: fbca04
================================================
FILE: .github/workflows/docs.yml
================================================
name: Docs
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
# workflow_run:
# workflows: ['Test']
# types:
# - completed
jobs:
build:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.VERCEL_TOKEN }}
TURBO_TEAM: ${{ secrets.VERCEL_TEAM }}
TURBO_CACHE_KEY: ubuntu-latest-16 # reuse cache key from ci workflow
NODE_OPTIONS: '--max_old_space_size=4096'
steps:
- uses: actions/checkout@v2
- name: Install pnpm
uses: pnpm/action-setup@v2.2.1
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: 16.x
registry-url: https://registry.npmjs.org/
cache: pnpm
- name: Install Dependencies and build all packages
run: pnpm bootstrap
# - name: deploy docs to vercel
# uses: BetaHuhn/deploy-to-vercel-action@v1
# # see: https://github.com/BetaHuhn/deploy-to-vercel-action
# with:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
# VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
# VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID_DOC_SITE }}
# # for team settings
# VERCEL_SCOPE: ${{ secrets.VERCEL_ORG_ID }}
# # the docs build dist folder
# WORKING_DIRECTORY: ./packages/doc/.vuepress/dist
# # bind domains
# # ALIAS_DOMAINS: |
# # docs.vr360.com
# # docs.vr360.cn
- name: deploy docs to vercel
uses: amondnet/vercel-action@v20
# see: https://github.com/amondnet/vercel-action
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID_DOC_SITE }}
# for team settings
scope: ${{ secrets.VERCEL_ORG_ID }}
# just like npx vercel --prod
vercel-args: '--prod'
# the docs build dist folder
working-directory: ./packages/doc/.vuepress/dist
# bind domains
# alias-domains: |
# docs.vr360.com
# docs.vr360.cn
================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
jobs:
build:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.VERCEL_TOKEN }}
TURBO_TEAM: ${{ secrets.VERCEL_TEAM }}
TURBO_CACHE_KEY: ubuntu-latest-16 # reuse cache key from ci workflow
steps:
- uses: actions/checkout@v2
- name: Install pnpm
uses: pnpm/action-setup@v2.2.1
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://registry.npmjs.org/
cache: pnpm
- name: Install Dependencies
run: pnpm bootstrap
- name: Lint
run: pnpm run lint
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.VERCEL_TOKEN }}
TURBO_TEAM: ${{ secrets.VERCEL_TEAM }}
TURBO_CACHE_KEY: ubuntu-latest-16 # reuse cache key from ci workflow
NODE_OPTIONS: '--max_old_space_size=4096'
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Install pnpm
uses: pnpm/action-setup@v2.2.1
- name: Use Node.js v16
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://registry.npmjs.org/
cache: pnpm
- run: npx conventional-github-releaser -p angular
continue-on-error: true
env:
CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Install Dependencies
run: pnpm i
- name: PNPM build
run: pnpm run build
- name: Publish to NPM
run: pnpm -r publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
# - name: Publish to VSCE & OVSX
# run: npm run publish
# working-directory: ./packages/vscode
# env:
# VSCE_TOKEN: ${{secrets.VSCE_TOKEN}}
# OVSX_TOKEN: ${{secrets.OVSX_TOKEN}}
================================================
FILE: .github/workflows/test.yml
================================================
name: Test
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
# workflow_run:
# workflows: ['Lint']
# types:
# - completed
jobs:
build:
runs-on: ${{ matrix.os }}
env:
TURBO_TOKEN: ${{ secrets.VERCEL_TOKEN }}
TURBO_TEAM: ${{ secrets.VERCEL_TEAM }}
TURBO_CACHE_KEY: ${{ matrix.os }}-${{ matrix.node-version }}
NODE_OPTIONS: '--max_old_space_size=4096'
strategy:
matrix:
node-version: [14, 16]
os: [ubuntu-latest, macOS-latest]
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Install pnpm
uses: pnpm/action-setup@v2.2.1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/
cache: pnpm
- name: Install Dependencies and build all packages
run: pnpm bootstrap
# - name: Build
# run: pnpm run build
- name: Test
run: pnpm run test
================================================
FILE: .gitignore
================================================
.DS_Store
.vite-ssg-dist
.vite-ssg-temp
*.local
dist
dist-ssr
node_modules
.idea/
*.log
stats.html
.vite-inspect
.history
**/.rollup.cache
**/test/coverage
tsconfig.tsbuildinfo
**/.temp
**/.cache
# testing
/coverage
cypress/videos
cypress/screenshots
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.vercel
.turbo
================================================
FILE: .husky/commit-msg
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# lint commit message
npx --no-install commitlint --edit "$1"
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# lint git stash files
npx --no-install lint-staged
================================================
FILE: .husky/prepare-commit-msg
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
echo "do not 'git commit' please run 'pnpm commit' instead"
# echo "commit lint please see https://juejin.cn/post/6934292467160514567#heading-3"
echo "See .github/commit-convention.md for more details."
================================================
FILE: .npmrc
================================================
# registry = "https://registry.npmmirror.com"
sass_binary_site = "https://cdn.npmmirror.com/binaries/node-sass"
phantomjs_cdnurl = "https://npmmirror.com/package/downloads"
electron_mirror = "https://registry.npmmirror.com/binary.html?path=electron/"
sqlite3_binary_host_mirror = "https://foxgis.oss-cn-shanghai.aliyuncs.com/"
profiler_binary_host_mirror = "https://registry.npmmirror.com/binary.html?path=node-inspector/"
chromedriver_cdnurl = "https://cdn.npmmirror.com/binaries/chromedriver"
# pnpm 子线程交叉打印
stream = true
# pnpm 工作区中的本地包是否优先于注册表中的包
prefer-workspace-packages = true
# pnpm 将 monorepo 工作区中的本地可用包链接到 node_modules,而不是从注册表重新下载它们。
link-workspace-packages = true
# 依赖提升
public-hoist-pattern[] = *types*
public-hoist-pattern[] = *eslint*
public-hoist-pattern[] = *stylelint*
public-hoist-pattern[] = @prettier/plugin-*
public-hoist-pattern[] = *prettier-plugin-*
public-hoist-pattern[] = prettier
public-hoist-pattern[] = *jest*
public-hoist-pattern[] = *rollup*
public-hoist-pattern[] = *babel*
public-hoist-pattern[] = core-js
public-hoist-pattern[] = regenerator-runtime
public-hoist-pattern[] = esbuild
public-hoist-pattern[] = *conventional*
public-hoist-pattern[] = jsdom
hoist-pattern[] = typescript
hoist-pattern[] = @vue/*
hoist-pattern[] = vue-template-compiler
hoist-pattern[] = vue-template-es2015-compiler
hoist-pattern[] = *vuepress*
hoist-pattern[] = *plop*
hoist-pattern[] = vue-demi
# for doc-site
hoist-pattern[] = nprogress
================================================
FILE: .stylelintignore
================================================
node_modules/
**/*.spec.*
es/
lib/
_site/
dist/
**/node_modules/*
**/segi-ant-theme.less
**/.rollup.cache/*
**/index.html
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"antfu.iconify",
"antfu.unocss",
"antfu.goto-alias",
"csstools.postcss",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"mrmlnc.vscode-less",
"mikestead.dotenv",
"stylelint.vscode-stylelint",
"vue.volar",
"streetsidesoftware.code-spell-checker"
]
}
================================================
FILE: .vscode/settings.json
================================================
{
// Use PNPM
"npm.packageManager": "pnpm",
// "eslint.packageManager": "pnpm",
"typescript.tsdk": "./node_modules/typescript/lib",
"editor.tabSize": 2,
"stylelint.enable": true,
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
"prettier.enable": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
},
"files.associations": {
"*.css": "postcss"
},
"typescript.inlayHints.parameterNames.enabled": "all",
// "typescript.inlayHints.variableTypes.enabled": true,
// "typescript.inlayHints.propertyDeclarationTypes.enabled": true,
"typescript.inlayHints.parameterTypes.enabled": true,
// "typescript.inlayHints.functionLikeReturnTypes.enabled": true,
"scss.lint.unknownAtRules": "ignore",
"less.lint.unknownAtRules": "ignore",
"css.lint.unknownAtRules": "ignore",
"unocss.root": "playgrounds/vue3",
"workbench.colorCustomizations": {
"activityBar.background": "#252F38",
"titleBar.activeBackground": "#34414F",
"titleBar.activeForeground": "#F9FAFB"
},
"cSpell.words": [
"alais",
"amondnet",
"antfu",
"apartuser",
"APPKEY",
"appr",
"Attributify",
"authc",
"axios",
"bonuse",
"bumpp",
"busi",
"Cascader",
"Certi",
"clsx",
"commitlint",
"Compat",
"cparagraph",
"csentence",
"cssnano",
"cust",
"cword",
"datacachesvr",
"Datetime",
"demi",
"docsearch",
"DOWNFILE",
"Dtos",
"ecmaversion",
"editble",
"envinfo",
"esno",
"Filterbar",
"globby",
"iconify",
"iife",
"INDIV",
"innercheckinmember",
"Inspction",
"intlify",
"jsdelivr",
"Jssdk",
"jweixin",
"Lazyload",
"micromessenger",
"mockjs",
"nicepkg",
"noscript",
"nouce",
"nprogress",
"oper",
"OVSX",
"Pagelist",
"pannellum",
"pano",
"pinia",
"pnpm",
"poppable",
"preinstall",
"prismjs",
"qrcode",
"raycaster",
"Realsee",
"redrun",
"restapi",
"rgba",
"safelist",
"Segi",
"semibold",
"shiki",
"Sname",
"solidjs",
"stackblitz",
"stackblitzrc",
"stylelint",
"svgr",
"tabbar",
"testop",
"threejs",
"touchmove",
"tweenjs",
"typecheck",
"typeof",
"uhome",
"uhomecp",
"unocss",
"unplugin",
"unproject",
"unref",
"userinfo",
"Vant",
"vconsole",
"VERCEL",
"Vite",
"vitejs",
"Vitesse",
"vitest",
"vpay",
"vuepress",
"vueuse",
"wcpay",
"webgl",
"Wechat",
"Weixin",
"wxappid",
"XFQYGM",
"XFQYXZ",
"yangjinming",
"zhong",
"ZLZYLX",
"ZZMM"
],
"commentTranslate.targetLanguage": "zh-CN"
}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at 2214962083@qq.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Thanks for being interested in contributing to this project!
## Development
To improve our development process, we've provide a playground, you can use any package in playground. Vr360 uses a monorepo structure and packages/package can be consumed in isolation.
## Setup
The following steps will get you up and running to contribute to Vr360:
1. Fork the repo (click the <kbd>Fork</kbd> button at the top right of
[this page](https://github.com/nicepkg/vr360))
2. Clone your fork locally
```sh
git clone https://github.com/<your_github_username>/vr360.git
cd vr360
```
3. Install Dependencies. This project depends on node v14+ and pnpm 6.x
If you don't have pnpm installed, you should execute:
```bash
npm i -g pnpm@6.32.17
```
Install the dependencies:
```bash
pnpm bootstrap
```
We use VuePress for rapid development and documenting. You can start it locally by
```bash
cd packages/doc-site
pnpm dev
```
### Commit Convention
Before you create a Pull Request, please check whether your commits comply with
the commit conventions used in this repository.
When you create a commit we kindly ask you to follow the convention
`category(scope or module): message` in your commit message while using one of
the following categories:
- `feat / feature`: all changes that introduce completely new code or new
features
- `fix`: changes that fix a bug (ideally you will additionally reference an
issue if present)
- `refactor`: any code related change that is not a fix nor a feature
- `docs`: changing existing or creating new documentation (i.e. README, docs for
usage of a lib or cli usage)
- `build`: all changes regarding the build of the software, changes to
dependencies or the addition of new dependencies
- `test`: all changes regarding tests (adding new tests or changing existing
ones)
- `ci`: all changes regarding the configuration of continuous integration (i.e.
github actions, ci system)
- `chore`: all changes to the repository that do not fit into any of the above
categories
If you are interested in the detailed specification you can visit
https://www.conventionalcommits.org/ or check out the
[Angular Commit Message Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines).
### Steps to PR
1. Fork of the vvr360 repository and clone your fork
2. Create a new branch out of the `master` branch. We follow the convention
`[type/scope]`. For example `fix/vr360-core` or `docs/vr360-ui`. `type`
can be either `docs`, `fix`, `feat`, `build`, or any other conventional
commit type. `scope` is just a short id that describes the scope of work.
3. Make and commit your changes following the
[commit convention](https://github.com/nicepkg/vr360/blob/master/CONTRIBUTING.md#commit-convention).
As you develop, you can run `pnpm --filter <module> build` and
`pnpm --filter <module> test` to make sure everything works as expected. Please
note that you might have to run `pnpm bootstrap` first in order to build all
dependencies.
## Code Style
Don't worry about the code style as long as you install the dev dependencies. Git hooks will format and fix them for you on committing.
## Thanks
Thank you again for being interested in this project! You are awesome!
## License
By contributing your code to the vr360 GitHub repository, you agree to
license your contribution under the MIT license.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 YangJinMing <https://github.com/2214962083>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<a href="https://vr360.nicepkg.cn/">
<img src="https://vr360.nicepkg.cn/images/logo-bg.png" width="50%">
</a>
<br>
<br>
<p>快速实现你的全景开发需求,全景看房、全景街景、全景景点</p>
<p>
<img src="https://img.shields.io/github/package-json/v/nicepkg/vr360" alt="version">
<img src="https://img.shields.io/github/license/nicepkg/vr360" alt="license">
<img src="https://img.shields.io/github/stars/nicepkg/vr360?style=social" alt="stars">
</p>
</div>
## 介绍
Vr360 是一个基于 threejs 能让你快速实现业务全景需求的库,比如全景看房、全景街景、全景景点。
它的核心被设计为框架无关性,可以用 json 配置的方式快速实现常见全景需求。
后续还会提供高度封装的 viewer 和 editor 等组件,很适合懒人,会提供 vue2/3 、 react 、web component 版本。
json 驱动视图的特性是好维护,你甚至可以不用接触 threejs。写出来的代码拉条哈士奇过来也能维护。
## 文档
[查看文档](https://vr360.nicepkg.cn/)
## 库列表
| 库名 | 文档 | 版本 | 描述 |
| -------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| [@nicepkg/vr360-core](./packages/vr360-core/README.md) | [链接](https://vr360.nicepkg.cn/libs/vr360-core/) | <img src="https://img.shields.io/npm/v/@nicepkg/vr360-core?style=flat-square" alt="version"> | json 驱动的全景浏库,设计框架无关性,可用于任何框架,如 vue/react/angular/ |
| [@nicepkg/vr360-ui](./packages/vr360-ui/README.md) | [链接](https://vr360.nicepkg.cn/libs/vr360-ui/) | <img src="https://img.shields.io/npm/v/@nicepkg/vr360-ui?style=flat-square" alt="version"> | (开发中...) 提供一个现成的 vr360 viewer 和 editor 组件,基于 stencil 构建的 web component。 |
| [@nicepkg/vr360-ui-vue2](./packages/vr360-ui-vue2/README.md) | [链接](https://vr360.nicepkg.cn/libs/vr360-ui-vue2/) | <img src="https://img.shields.io/npm/v/@nicepkg/vr360-ui-vue2?style=flat-square" alt="version"> | (开发中...) vr360-ui 的 vue2 二次封装版 |
| [@nicepkg/vr360-ui-vue3](./packages/vr360-ui-vue3/README.md) | [链接](https://vr360.nicepkg.cn/libs/vr360-ui-vue3/) | <img src="https://img.shields.io/npm/v/@nicepkg/vr360-ui-vue3?style=flat-square" alt="version"> | (开发中...) vr360-ui 的 vue3 二次封装版本,开箱即用。 |
| [@nicepkg/vr360-ui-react](./packages/vr360-ui-react/README.md) | [链接](https://vr360.nicepkg.cn/libs/vr360-ui-react/) | <img src="https://img.shields.io/npm/v/@nicepkg/vr360-ui-react?style=flat-square" alt="version"> | (开发中...) vr360-ui 的 react 二次封装版本,开箱即用。 |
## Contributing
Learn about contribution [here](https://github.com/nicepkg/vr360/blob/master/CONTRIBUTING.md).
This project exists thanks to all the people who contribute:
<a href="https://github.com/nicepkg/vr360/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nicepkg/vr360" />
</a>
## License
[MIT](https://github.com/nicepkg/vr360/blob/master/LICENSE) License © 2022-PRESENT [nicepkg](https://github.com/nicepkg)
================================================
FILE: commitlint.config.js
================================================
module.exports = {
extends: ['@commitlint/config-conventional']
}
================================================
FILE: lint-staged.config.js
================================================
module.exports = {
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
'*.json': ['prettier --write'],
'*.vue': ['eslint --fix', 'stylelint --aei --fix', 'prettier --write'],
'*.{scss,sass,less,css}': ['stylelint --aei --fix', 'prettier --write'],
'*.md': ['prettier --write']
}
================================================
FILE: netlify.toml
================================================
[build.environment]
NODE_VERSION = "16"
NPM_FLAGS = "--version" # prevent Netlify npm install
[build]
publish = "packages/doc/.vuepress/dist"
command = "npm i -g pnpm && pnpm build:ci"
================================================
FILE: package.json
================================================
{
"name": "@nicepkg/vr360",
"version": "0.3.1",
"private": true,
"packageManager": "pnpm@7.0.0",
"author": "yangjinming",
"description": "快速实现你的全景开发需求,全景看房、全景街景、全景景点",
"engines": {
"node": ">=14",
"pnpm": ">=7"
},
"scripts": {
"bootstrap": "pnpm i &&pnpm build:all",
"build": "esno ./scripts/build.ts",
"build:all": "turbo run build",
"build:ci": "pnpm i --store=node_modules/.pnpm-store --frozen-lockfile && turbo run build --no-cache",
"check-update": "esno ./scripts/check-update.ts",
"clean": "rimraf **/node_modules/**",
"commit": "git add . &&git-cz",
"lint": "pnpm lint:es &&pnpm lint:css",
"lint:change": "lint-staged",
"lint:css": "stylelint --aei --fix ./**/*.{vue,css,sass,scss,less,html} --cache --cache-location node_modules/.cache/stylelint/",
"lint:es": "eslint --fix . --ext .jsx,.js,.vue,.ts,.tsx",
"preinstall": "npx only-allow pnpm",
"prepare": "husky install",
"release": "esno ./scripts/release.ts",
"test": "pnpm run -r test"
},
"devDependencies": {
"@commitlint/cli": "^17.1.2",
"@commitlint/config-conventional": "^17.1.0",
"@types/eslint": "^8.4.6",
"@types/lodash-es": "^4.17.6",
"@types/node": "*",
"@types/prettier": "^2.7.1",
"@typescript-eslint/eslint-plugin": "^5.38.1",
"@typescript-eslint/parser": "^5.38.1",
"@vue/eslint-config-typescript": "^11.0.2",
"bumpp": "^8.2.1",
"chalk": "4.1.2",
"commitizen": "^4.2.5",
"confusing-browser-globals": "^1.0.11",
"conventional-changelog-cli": "^2.2.2",
"cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.24.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.1",
"eslint-plugin-eslint-comments": "3.2.0",
"eslint-plugin-html": "^7.1.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest-dom": "^4.0.2",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "7.31.7",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-testing-library": "^5.7.0",
"eslint-plugin-vue": "^9.5.1",
"eslint-plugin-unicorn": "43.0.2",
"esno": "*",
"globby": "11.1.0",
"husky": "^8.0.1",
"ini": "^3.0.1",
"less": "^4.1.3",
"lint-staged": "^13.0.3",
"npm-check-updates": "^16.3.3",
"npm-run-all": "^4.1.5",
"ora": "6.1.2",
"plop": "^3.1.1",
"postcss": "^8.4.16",
"postcss-html": "^1.5.0",
"postcss-less": "^6.0.0",
"prettier": "^2.7.1",
"react": "*",
"rimraf": "^3.0.2",
"stylelint": "^14.13.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recess-order": "^3.0.0",
"stylelint-config-recommended": "^9.0.0",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^28.0.0",
"stylelint-declaration-block-no-ignored-properties": "^2.5.0",
"stylelint-less": "^1.0.6",
"stylelint-order": "^5.0.0",
"stylelint-prettier": "^2.0.0",
"turbo": "^1.5.4",
"typescript": "*"
},
"pnpm": {
"overrides": {
"@testing-library/dom": "8.18.1",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
"@types/node": "18.7.23",
"@types/react": "18.0.21",
"@types/react-dom": "18.0.6",
"@types/react-router-dom": "5.3.3",
"@vitejs/plugin-react": "2.1.0",
"concurrently": "7.4.0",
"conventional-changelog-cli": "2.2.2",
"esno": "0.16.3",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-router-dom": "6.3.0",
"typescript": "4.7.4",
"vite": "3.0.9",
"vitest": "0.23.4",
"vue-demi": "0.13.11"
},
"peerDependencyRules": {
"allowedVersions": {
"react": "17",
"react-dom": "17",
"typescript": "4.8"
}
},
"allowedDeprecatedVersions": {
"stable": "*",
"core-js": "*",
"mkdirp": "*",
"uuid": "*",
"querystring": "*",
"sane": "*",
"chokidar": "*",
"fsevents": "*",
"source-map-resolve": "*",
"source-map-url": "*",
"resolve-url": "*",
"urix": "*"
},
"packageExtensions": {
"stylelint-config-recommended-vue": {
"dependencies": {
"postcss-html": "^1.4.1"
}
},
"vue-template-compiler": {
"devDependencies": {
"vue": "^2.6.14"
},
"peerDependencies": {
"vue": "^2.6.14"
}
}
}
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
================================================
FILE: packages/doc/.vuepress/client.ts
================================================
/* eslint-disable unicorn/no-await-expression-member */
import type {ProjectFiles} from '@stackblitz/sdk'
import {defineClientConfig} from '@vuepress/client'
import pkg from '../package.json'
export default defineClientConfig({
enhance({app}) {
app.config.globalProperties.version = pkg.version
if (!__VUEPRESS_SSR__) {
window.loadCodeDemoModeDefaultFiles = async _mode => {
const mode = _mode as 'node' | 'vue2' | 'vue3' | 'react' | 'html'
const defaultFiles: ProjectFiles = {
'src/demo.css': (await import('./public/code-demo-templates/demo.css.txt?raw')).default,
'.stackblitzrc': `{
"startCommand": "npm run dev"
}`
}
switch (mode) {
case 'node':
case 'vue3':
return {
...defaultFiles,
'src/App.vue': (await import('./public/code-demo-templates/vue3/src/App.vue.txt?raw')).default,
'src/main.ts': (await import('./public/code-demo-templates/vue3/src/main.ts.txt?raw')).default,
'types/module.d.ts': (await import('./public/code-demo-templates/vue3/types/module.d.ts.txt?raw'))
.default,
'index.html': (await import('./public/code-demo-templates/vue3/index.html.txt?raw')).default,
'vite.config.ts': (await import('./public/code-demo-templates/vue3/vite.config.ts.txt?raw')).default,
'package.json': (await import('./public/code-demo-templates/vue3/package.json.txt?raw')).default,
'tsconfig.json': (await import('./public/code-demo-templates/vue3/tsconfig.json.txt?raw')).default
} as ProjectFiles
case 'vue2':
return {
...defaultFiles,
'src/App.vue': (await import('./public/code-demo-templates/vue2/src/App.vue.txt?raw')).default,
'src/main.ts': (await import('./public/code-demo-templates/vue2/src/main.ts.txt?raw')).default,
'types/module.d.ts': (await import('./public/code-demo-templates/vue2/types/module.d.ts.txt?raw'))
.default,
'index.html': (await import('./public/code-demo-templates/vue2/index.html.txt?raw')).default,
'vite.config.ts': (await import('./public/code-demo-templates/vue2/vite.config.ts.txt?raw')).default,
'package.json': (await import('./public/code-demo-templates/vue2/package.json.txt?raw')).default,
'tsconfig.json': (await import('./public/code-demo-templates/vue2/tsconfig.json.txt?raw')).default
} as ProjectFiles
case 'react':
return {
...defaultFiles,
'src/main.tsx': (await import('./public/code-demo-templates/react/src/main.tsx.txt?raw')).default,
'index.html': (await import('./public/code-demo-templates/react/index.html.txt?raw')).default,
'package.json': (await import('./public/code-demo-templates/react/package.json.txt?raw')).default,
'tsconfig.json': (await import('./public/code-demo-templates/react/tsconfig.json.txt?raw')).default,
'vite.config.ts': (await import('./public/code-demo-templates/react/vite.config.ts.txt?raw')).default
} as ProjectFiles
case 'html':
return {
'package.json': (await import('./public/code-demo-templates/html/package.json.txt?raw')).default,
'demo.css': (await import('./public/code-demo-templates/demo.css.txt?raw')).default,
'.stackblitzrc': `{
"startCommand": "npm run dev"
}`
} as ProjectFiles
default:
return {}
}
}
}
}
})
================================================
FILE: packages/doc/.vuepress/components/DemoA.vue
================================================
<template>
<div class="demoA">
<div
ref="tipRef"
class="demoA-tip"
:style="{
transform: `translate(${tipLeft}px, ${tipTop + 50}px)`,
zIndex: showTip ? 99 : -1,
visibility: showTip ? 'visible' : 'hidden'
}"
>
<div class="demoA-tip-title">{{ tipTitle }}</div>
<div class="demoA-tip-content">{{ tipContent }}</div>
</div>
<div ref="containerRef" class="demoA-container"></div>
<div class="demoA-bottom-bar">
<div
v-for="space in spacesConfig"
:key="space.id"
class="demoA-bottom-card"
:style="{
backgroundImage: `url(${space.cubeSpaceTextureUrls.left})`
}"
@click="handleSwitchSpace(space)"
></div>
</div>
</div>
</template>
<script setup lang="ts">
/* eslint-disable import/no-named-as-default-member */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import {onMounted, ref} from 'vue'
import type {SpaceConfig} from '@nicepkg/vr360-core'
import {Vr360} from '@nicepkg/vr360-core'
import textures from '../../../../textures.json'
const containerRef = ref<HTMLElement>()
const tipRef = ref<HTMLElement>()
const tipLeft = ref(0)
const tipTop = ref(0)
const showTip = ref(false)
const tipTitle = ref('')
const tipContent = ref('')
let vr360: InstanceType<typeof Vr360>
const spacesConfig = ref<SpaceConfig[]>([
{
id: 'spaceA',
tips: [
{
id: '1',
position: {x: 0, y: -10, z: 40},
content: {
title: '豪华跑车',
text: '比奥迪还贵的豪华跑车'
}
},
{
id: '2',
textureUrl: textures.hotpot,
targetSpaceId: 'spaceB',
position: {x: -10, y: -4, z: 40},
content: {
title: '去客厅',
text: '一起去尊贵的客厅吧'
}
}
],
cubeSpaceTextureUrls: textures.firstHouseDoor
},
{
id: 'spaceB',
tips: [
{
id: '3',
position: {x: -2, y: -25, z: 40},
content: {
title: '香奈儿垃圾桶',
text: '里面装着主人不用的奢侈品'
}
},
{
id: '4',
position: {x: -20, y: 0, z: 40},
content: {
title: '宇宙牌冰箱',
text: '装着超级多零食'
}
},
{
id: '5',
textureUrl: textures.hotpot,
targetSpaceId: 'spaceA',
position: {
x: -8,
y: 0,
z: -40
},
content: {
title: '去门口',
text: '一起去门口吧'
}
}
],
cubeSpaceTextureUrls: textures.firstHouseLivingRoom
}
])
function handleSwitchSpace(space: SpaceConfig) {
vr360.switchSpace(space.id)
}
onMounted(() => {
vr360 = new Vr360({
container: containerRef.value!,
tipContainer: tipRef.value!,
spacesConfig: spacesConfig.value
})
vr360.controls.autoRotate = true
vr360.render()
vr360.listenResize()
vr360.on('showTip', e => {
vr360!.controls.autoRotate = false
const {top, left, tip} = e
showTip.value = true
tipLeft.value = left
tipTop.value = top
tipTitle.value = tip.content.title
tipContent.value = tip.content.text
})
vr360.on('hideTip', () => {
vr360!.controls.autoRotate = true
showTip.value = false
})
})
</script>
<style>
.demoA {
position: relative;
z-index: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
width: 100%;
max-width: calc(100vw - 3rem);
height: 500px;
margin-bottom: 2rem;
overflow: hidden;
background-color: var(--c-bg);
border: 1px solid var(--c-border);
border-radius: 8px;
}
.demoA-tip {
position: absolute;
top: 0;
left: 0;
z-index: -1;
display: flex;
flex-direction: column;
justify-content: center;
width: 240px;
height: 60px;
padding: 4px;
color: #fff;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 4px;
}
.demoA-tip-title {
font-weight: bold;
}
.demoA-container {
width: 100%;
height: 100%;
}
.demoA-bottom-bar {
display: flex;
align-items: center;
width: 100%;
height: 100px;
}
.demoA-bottom-card {
width: 140px;
height: 70px;
margin-left: 1rem;
cursor: pointer;
background-repeat: no-repeat;
background-size: cover;
border-radius: 4px;
}
</style>
================================================
FILE: packages/doc/.vuepress/components/NpmBadge.vue
================================================
<script setup lang="ts">
import {computed} from 'vue'
const props = defineProps({
package: {
type: String,
required: true
},
distTag: {
type: String,
required: false,
default: 'next'
}
})
const badgeLink = computed(() => `https://www.npmjs.com/package/${props.package}`)
const badgeLabel = computed(() => {
if (props.distTag) {
return `${props.package}@${props.distTag}`
}
return props.package
})
const badgeImg = computed(
() => `https://badgen.net/npm/v/${props.package}/${props.distTag}?label=${encodeURIComponent(badgeLabel.value)}`
)
</script>
<template>
<a class="npm-badge" :href="badgeLink" :title="package" target="_blank" rel="noopener noreferrer">
<img :src="badgeImg" :alt="package" />
</a>
</template>
<style scoped>
.npm-badge {
margin-right: 0.5rem;
}
</style>
================================================
FILE: packages/doc/.vuepress/configs/index.ts
================================================
export * as navbar from './navbar'
export * as sidebar from './sidebar'
================================================
FILE: packages/doc/.vuepress/configs/navbar/en.ts
================================================
import type {NavbarConfig} from '@vuepress/theme-default'
import {version} from '../../utils/common'
export const en: NavbarConfig = [
{
text: 'Guide',
link: '/en/guide/'
},
{
text: `v${version}`,
children: [
{
text: 'Releases',
link: 'https://github.com/nicepkg/vr360/releases'
}
]
}
]
================================================
FILE: packages/doc/.vuepress/configs/navbar/index.ts
================================================
export * from './en'
export * from './zh'
================================================
FILE: packages/doc/.vuepress/configs/navbar/zh.ts
================================================
import type {NavbarConfig} from '@vuepress/theme-default'
import {version} from '../../utils/common'
export const zh: NavbarConfig = [
{
text: '指南',
link: '/guide/'
},
{
text: '库列表',
children: [
{
text: 'vr360-core',
link: '/libs/vr360-core/README.md'
}
]
},
{
text: `v${version}`,
children: [
{
text: '更新日志',
link: 'https://github.com/nicepkg/vr360/releases'
}
]
}
]
================================================
FILE: packages/doc/.vuepress/configs/sidebar/en.ts
================================================
import type {SidebarConfig} from '@vuepress/theme-default'
export const en: SidebarConfig = {
'/en/guide/': [
{
text: 'Guide',
children: ['/en/guide/README.md', '/en/guide/questions.md']
}
]
}
================================================
FILE: packages/doc/.vuepress/configs/sidebar/index.ts
================================================
export * from './en'
export * from './zh'
================================================
FILE: packages/doc/.vuepress/configs/sidebar/zh.ts
================================================
import type {SidebarConfig} from '@vuepress/theme-default'
export const zh: SidebarConfig = {
'/guide/': [
{
text: '指南',
children: ['/guide/README.md', '/guide/questions.md']
}
],
'/libs/vr360-core/': [
{
text: 'vr360-core',
children: [
'/libs/vr360-core/README.md',
'/libs/vr360-core/properties.md',
'/libs/vr360-core/methods.md',
'/libs/vr360-core/events.md',
'/libs/vr360-core/example.md'
]
}
]
}
================================================
FILE: packages/doc/.vuepress/examples/firstHouse/demo.css
================================================
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
.demo {
position: relative;
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: #fff;
}
.demo-tip {
position: absolute;
top: 0;
left: 0;
z-index: -1;
display: flex;
flex-direction: column;
justify-content: center;
width: 240px;
height: 60px;
padding: 4px;
color: #fff;
cursor: pointer;
visibility: hidden;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 4px;
}
.demo-tip-title {
font-weight: bold;
}
.demo-container {
width: 100%;
height: 100%;
}
.demo-bottom-bar {
display: flex;
align-items: center;
width: 100%;
height: 100px;
}
.demo-bottom-card {
width: 140px;
height: 70px;
margin-left: 1rem;
cursor: pointer;
background-repeat: no-repeat;
background-size: cover;
border-radius: 4px;
}
================================================
FILE: packages/doc/.vuepress/examples/firstHouse/html.html
================================================
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Html App</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<!-- 引入 threejs -->
<script src="https://unpkg.com/three@0.145.0/build/three.min.js"></script>
<!-- 引入 vr360-core -->
<script src="https://unpkg.com/@nicepkg/vr360-core@^0.3.1/dist/index.min.umd.js"></script>
<!-- 引入自己的 css -->
<link href="./demo.css" rel="stylesheet" />
</head>
<body>
<div class="demo">
<!-- 提示 -->
<div class="demo-tip">
<!-- 提示标题 -->
<div class="demo-tip-title"></div>
<!-- 提示内容 -->
<div class="demo-tip-content"></div>
</div>
<!-- 360全景容器 -->
<div class="demo-container"></div>
<!-- 底部切换场景 -->
<div class="demo-bottom-bar"></div>
</div>
<script>
const {Vr360} = Vr360Core // 原生使用时,所有的导出都在 window.Vr360Core 里
const container = document.querySelector('.demo-container') // 360全景容器
const tip = document.querySelector('.demo-tip') // 提示容器
const tipTitle = document.querySelector('.demo-tip-title') // 提示标题 el
const tipContent = document.querySelector('.demo-tip-content') // 提示内容 el
const bottomBar = document.querySelector('.demo-bottom-bar') // 底部切换场景容器
// 全景空间配置
const spacesConfig = [
{
id: 'spaceA', // 空间 id,用于切换空间,必须唯一
tips: [
// 提示,可选
{
id: '1', // 提示 id,用于缓存,在当前 tips 数组里要唯一,必须
position: {x: 0, y: -10, z: 40}, // 提示位置,必须
content: {
// 提示内容,在 showTip 事件会暴露,要包含什么你自己决定。
title: '豪华跑车',
text: '比奥迪还贵的豪华跑车'
}
},
{
id: '2',
// 自定义提示图标贴图,可选
textureUrl:
'https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png',
targetSpaceId: 'spaceB', // 提示点击后跳转的空间 id,可选
position: {x: -10, y: -4, z: 40},
content: {
title: '去客厅',
text: '一起去尊贵的客厅吧'
}
}
],
cubeSpaceTextureUrls: {
// 立方体贴图,分别为:背侧、下侧、前侧、左侧、右侧、上侧,必须
back: 'https://m.360buyimg.com/babel/jfs/t1/40814/31/19646/41953/63398297E0707fe35/4e831e60cf579899.jpg',
down: 'https://m.360buyimg.com/babel/jfs/t1/43941/23/19369/84038/633982d7E838acd9a/9e7a89cc910d3409.jpg',
front: 'https://m.360buyimg.com/babel/jfs/t1/150573/35/27827/113528/633982faE8556c0c0/b455284fd91885c6.jpg',
left: 'https://m.360buyimg.com/babel/jfs/t1/189204/25/29491/61430/63398310E3c180e43/26d9b459cf714f9f.jpg',
right: 'https://m.360buyimg.com/babel/jfs/t1/184948/34/28448/131232/63398323E61ff80fb/89ab84eda0421260.jpg',
up: 'https://m.360buyimg.com/babel/jfs/t1/86258/24/33602/70616/63398334Ea2dcf448/e2f5c275792fb3d6.jpg'
}
},
{
id: 'spaceB',
tips: [
{
id: '3',
position: {x: -2, y: -25, z: 40},
content: {
title: '香奈儿垃圾桶',
text: '里面装着主人不用的奢侈品'
}
},
{
id: '4',
position: {x: -20, y: 0, z: 40},
content: {
title: '宇宙牌冰箱',
text: '装着超级多零食'
}
},
{
id: '5',
textureUrl:
'https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png',
targetSpaceId: 'spaceA',
position: {
x: -8,
y: 0,
z: -40
},
content: {
title: '去门口',
text: '一起去门口吧'
}
}
],
cubeSpaceTextureUrls: {
back: 'https://m.360buyimg.com/babel/jfs/t1/48117/28/21445/120448/63398366Ede81497b/a46e362df5f7d0ed.jpg',
down: 'https://m.360buyimg.com/babel/jfs/t1/101209/24/26762/106253/63398376Eedb0db22/4f335c4ecd72ad74.jpg',
front: 'https://m.360buyimg.com/babel/jfs/t1/154056/6/26449/110652/63398388E8ecda044/22e1646534839a95.jpg',
left: 'https://m.360buyimg.com/babel/jfs/t1/198990/28/28201/74687/6339839aE28806a5e/43b311d3379397df.jpg',
right: 'https://m.360buyimg.com/babel/jfs/t1/209711/14/25233/92186/633983b0E8f4df687/750ba84061ea64a6.jpg',
up: 'https://m.360buyimg.com/babel/jfs/t1/186545/35/29054/29678/633983c2E72ef4848/92043b945a03fc29.jpg'
}
}
]
// 初始化全景实例
const vr360 = new Vr360({
container, // 全景挂载容器
tipContainer: tip, // 提示挂载容器
spacesConfig // 全景配置
})
// 设置全景自动旋转
vr360.controls.autoRotate = true
// 开始渲染全景
vr360.render()
// 实时监听页面尺寸变化,更新全景尺寸
vr360.listenResize()
// 当需要显示提示时
vr360.on('showTip', e => {
// 停止旋转场景
vr360.controls.autoRotate = false
// 设置提示内容和提示容器位置
const {top, left} = e
Object.assign(tip.style, {
transform: `translate(${left}px, ${top + 50}px)`,
zIndex: 99,
visibility: 'visible'
})
tip.style.t
tipTitle.innerText = e.tip.content.title
tipContent.innerText = e.tip.content.text
})
// 当需要隐藏提示时
vr360.on('hideTip', () => {
// 重新开启旋转场景
vr360.controls.autoRotate = true
// 隐藏提示容器
tip.style.zIndex = -1
tip.style.visibility = 'hidden'
})
// 页面卸载时注销全景实例
window.addEventListener('unload', () => {
vr360.destroy()
})
// 切换全景空间
function handleSwitchSpace(space) {
vr360.switchSpace(space.id)
}
bottomBar.append(
...spacesConfig.map(space => {
const card = document.createElement('div')
card.className = 'demo-bottom-card'
Object.assign(card.style, {
backgroundImage: `url(${space.cubeSpaceTextureUrls.left})`
})
card.addEventListener('click', () => {
handleSwitchSpace(space)
})
return card
})
)
</script>
</body>
</html>
================================================
FILE: packages/doc/.vuepress/examples/firstHouse/react.tsx
================================================
import React, {useEffect, useState, useRef} from 'react'
import {Vr360} from '@nicepkg/vr360-core'
import type {SpaceConfig} from '@nicepkg/vr360-core'
import './demo.css' // 引入自己的样式
function Example() {
const containerRef = useRef<HTMLDivElement>(null) // 全景容器
const tipRef = useRef<HTMLDivElement>(null) // 提示容器
const [tipLeft, setTipLeft] = useState(0) // 提示容器的 left 或 translateX 值
const [tipTop, setTipTop] = useState(0) // 提示容器的 top 或 translateY 值
const [showTip, setShowTip] = useState(false) // 是否显示提示容器
const [tipTitle, setTipTitle] = useState('') // 提示容器的标题
const [tipContent, setTipContent] = useState('') // 提示容器的内容
const [vr360, setVr360] = useState<InstanceType<typeof Vr360>>() // 全景实例
// 全景空间配置
const spacesConfig: SpaceConfig[] = [
{
id: 'spaceA', // 空间 id,用于切换空间,必须唯一
tips: [
// 提示,可选
{
id: '1', // 提示 id,用于缓存,在当前 tips 数组里要唯一,必须
position: {x: 0, y: -10, z: 40}, // 提示位置,必须
content: {
// 提示内容,在 showTip 事件会暴露,要包含什么你自己决定。
title: '豪华跑车',
text: '比奥迪还贵的豪华跑车'
}
},
{
id: '2',
// 自定义提示图标贴图,可选
textureUrl:
'https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png',
targetSpaceId: 'spaceB', // 提示点击后跳转的空间 id,可选
position: {x: -10, y: -4, z: 40},
content: {
title: '去客厅',
text: '一起去尊贵的客厅吧'
}
}
],
cubeSpaceTextureUrls: {
// 立方体贴图,分别为:背侧、下侧、前侧、左侧、右侧、上侧,必须
back: 'https://m.360buyimg.com/babel/jfs/t1/40814/31/19646/41953/63398297E0707fe35/4e831e60cf579899.jpg',
down: 'https://m.360buyimg.com/babel/jfs/t1/43941/23/19369/84038/633982d7E838acd9a/9e7a89cc910d3409.jpg',
front: 'https://m.360buyimg.com/babel/jfs/t1/150573/35/27827/113528/633982faE8556c0c0/b455284fd91885c6.jpg',
left: 'https://m.360buyimg.com/babel/jfs/t1/189204/25/29491/61430/63398310E3c180e43/26d9b459cf714f9f.jpg',
right: 'https://m.360buyimg.com/babel/jfs/t1/184948/34/28448/131232/63398323E61ff80fb/89ab84eda0421260.jpg',
up: 'https://m.360buyimg.com/babel/jfs/t1/86258/24/33602/70616/63398334Ea2dcf448/e2f5c275792fb3d6.jpg'
}
},
{
id: 'spaceB',
tips: [
{
id: '3',
position: {x: -2, y: -25, z: 40},
content: {
title: '香奈儿垃圾桶',
text: '里面装着主人不用的奢侈品'
}
},
{
id: '4',
position: {x: -20, y: 0, z: 40},
content: {
title: '宇宙牌冰箱',
text: '装着超级多零食'
}
},
{
id: '5',
textureUrl:
'https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png',
targetSpaceId: 'spaceA',
position: {
x: -8,
y: 0,
z: -40
},
content: {
title: '去门口',
text: '一起去门口吧'
}
}
],
cubeSpaceTextureUrls: {
back: 'https://m.360buyimg.com/babel/jfs/t1/48117/28/21445/120448/63398366Ede81497b/a46e362df5f7d0ed.jpg',
down: 'https://m.360buyimg.com/babel/jfs/t1/101209/24/26762/106253/63398376Eedb0db22/4f335c4ecd72ad74.jpg',
front: 'https://m.360buyimg.com/babel/jfs/t1/154056/6/26449/110652/63398388E8ecda044/22e1646534839a95.jpg',
left: 'https://m.360buyimg.com/babel/jfs/t1/198990/28/28201/74687/6339839aE28806a5e/43b311d3379397df.jpg',
right: 'https://m.360buyimg.com/babel/jfs/t1/209711/14/25233/92186/633983b0E8f4df687/750ba84061ea64a6.jpg',
up: 'https://m.360buyimg.com/babel/jfs/t1/186545/35/29054/29678/633983c2E72ef4848/92043b945a03fc29.jpg'
}
}
]
useEffect(() => {
// 初始化全景实例
setVr360(
new Vr360({
container: containerRef.current!,
tipContainer: tipRef.current!,
spacesConfig
})
)
return () => {
// 页面卸载时注销全景实例
vr360?.destroy?.()
}
}, [])
useEffect(() => {
if (vr360) {
// 设置全景自动旋转
vr360.controls.autoRotate = true
// 开始渲染全景
vr360.render()
// 实时监听页面尺寸变化,更新全景尺寸
vr360.listenResize()
// 当需要显示提示时
vr360.on('showTip', e => {
// 停止旋转场景
vr360!.controls.autoRotate = false
// 设置提示内容和提示容器位置
const {top, left, tip} = e
setShowTip(true)
setTipLeft(left)
setTipTop(top)
setTipTitle(tip.content.title)
setTipContent(tip.content.text)
})
// 当需要隐藏提示时
vr360.on('hideTip', () => {
// 重新开启旋转场景
vr360!.controls.autoRotate = true
// 隐藏提示容器
setShowTip(false)
})
}
}, [vr360])
// 切换全景空间
function handleSwitchSpace(space: SpaceConfig) {
vr360?.switchSpace?.(space.id)
}
return (
<div className="demo">
{/* 提示 */}
<div
ref={tipRef}
className="demo-tip"
style={{
transform: `translate(${tipLeft}px, ${tipTop + 50}px)`,
zIndex: showTip ? 99 : -1,
visibility: showTip ? 'visible' : 'hidden'
}}
>
{/* 提示标题 */}
<div className="demo-tip-title">{tipTitle}</div>
{/* 提示内容 */}
<div className="demo-tip-content">{tipContent}</div>
</div>
{/* 360全景容器 */}
<div ref={containerRef} className="demo-container"></div>
{/* 底部切换场景 */}
<div className="demo-bottom-bar">
{spacesConfig.map(space => (
<div
key={space.id}
className="demo-bottom-card"
onClick={() => handleSwitchSpace(space)}
style={{
backgroundImage: `url(${space.cubeSpaceTextureUrls.left})`
}}
></div>
))}
</div>
</div>
)
}
export default Example
================================================
FILE: packages/doc/.vuepress/examples/firstHouse/vue2.vue
================================================
<template>
<div class="demo">
<!-- 提示 -->
<div
ref="tipRef"
class="demo-tip"
:style="{
transform: `translate(${tip.left}px, ${tip.top + 50}px)`,
zIndex: tip.show ? 99 : -1,
visibility: tip.show ? 'visible' : 'hidden'
}"
>
<!-- 提示标题 -->
<div class="demo-tip-title">{{ tip.title }}</div>
<!-- 提示内容 -->
<div class="demo-tip-content">{{ tip.content }}</div>
</div>
<!-- 360全景容器 -->
<div ref="containerRef" class="demo-container"></div>
<!-- 底部切换场景 -->
<div class="demo-bottom-bar">
<div
v-for="space in spacesConfig"
:key="space.id"
class="demo-bottom-card"
@click="handleSwitchSpace(space)"
:style="{
backgroundImage: `url(${space.cubeSpaceTextureUrls.left})`
}"
></div>
</div>
</div>
</template>
<script lang="ts">
import {Vr360} from '@nicepkg/vr360-core'
import type {SpaceConfig} from '@nicepkg/vr360-core'
export default {
data() {
// 全景空间配置
const spacesConfig: SpaceConfig[] = [
{
id: 'spaceA', // 空间 id,用于切换空间,必须唯一
tips: [
// 提示,可选
{
id: '1', // 提示 id,用于缓存,在当前 tips 数组里要唯一,必须
position: {x: 0, y: -10, z: 40}, // 提示位置,必须
content: {
// 提示内容,在 showTip 事件会暴露,要包含什么你自己决定。
title: '豪华跑车',
text: '比奥迪还贵的豪华跑车'
}
},
{
id: '2',
// 自定义提示图标贴图,可选
textureUrl:
'https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png',
targetSpaceId: 'spaceB', // 提示点击后跳转的空间 id,可选
position: {x: -10, y: -4, z: 40},
content: {
title: '去客厅',
text: '一起去尊贵的客厅吧'
}
}
],
cubeSpaceTextureUrls: {
// 立方体贴图,分别为:背侧、下侧、前侧、左侧、右侧、上侧,必须
back: 'https://m.360buyimg.com/babel/jfs/t1/40814/31/19646/41953/63398297E0707fe35/4e831e60cf579899.jpg',
down: 'https://m.360buyimg.com/babel/jfs/t1/43941/23/19369/84038/633982d7E838acd9a/9e7a89cc910d3409.jpg',
front: 'https://m.360buyimg.com/babel/jfs/t1/150573/35/27827/113528/633982faE8556c0c0/b455284fd91885c6.jpg',
left: 'https://m.360buyimg.com/babel/jfs/t1/189204/25/29491/61430/63398310E3c180e43/26d9b459cf714f9f.jpg',
right: 'https://m.360buyimg.com/babel/jfs/t1/184948/34/28448/131232/63398323E61ff80fb/89ab84eda0421260.jpg',
up: 'https://m.360buyimg.com/babel/jfs/t1/86258/24/33602/70616/63398334Ea2dcf448/e2f5c275792fb3d6.jpg'
}
},
{
id: 'spaceB',
tips: [
{
id: '3',
position: {x: -2, y: -25, z: 40},
content: {
title: '香奈儿垃圾桶',
text: '里面装着主人不用的奢侈品'
}
},
{
id: '4',
position: {x: -20, y: 0, z: 40},
content: {
title: '宇宙牌冰箱',
text: '装着超级多零食'
}
},
{
id: '5',
textureUrl:
'https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png',
targetSpaceId: 'spaceA',
position: {
x: -8,
y: 0,
z: -40
},
content: {
title: '去门口',
text: '一起去门口吧'
}
}
],
cubeSpaceTextureUrls: {
back: 'https://m.360buyimg.com/babel/jfs/t1/48117/28/21445/120448/63398366Ede81497b/a46e362df5f7d0ed.jpg',
down: 'https://m.360buyimg.com/babel/jfs/t1/101209/24/26762/106253/63398376Eedb0db22/4f335c4ecd72ad74.jpg',
front: 'https://m.360buyimg.com/babel/jfs/t1/154056/6/26449/110652/63398388E8ecda044/22e1646534839a95.jpg',
left: 'https://m.360buyimg.com/babel/jfs/t1/198990/28/28201/74687/6339839aE28806a5e/43b311d3379397df.jpg',
right: 'https://m.360buyimg.com/babel/jfs/t1/209711/14/25233/92186/633983b0E8f4df687/750ba84061ea64a6.jpg',
up: 'https://m.360buyimg.com/babel/jfs/t1/186545/35/29054/29678/633983c2E72ef4848/92043b945a03fc29.jpg'
}
}
]
return {
vr360: null as InstanceType<typeof Vr360> | null, // 全景实例
spacesConfig, // 全景空间配置
tip: {
// 提示属性
top: 0, // 提示容器的 top 或 translateY 值
left: 0, // 提示容器的 left 或 translateX 值
title: '', // 提示标题
content: '', // 提示内容
show: false // 是否显示提示
}
}
},
mounted() {
// 初始化全景实例
this.vr360 = new Vr360({
container: this.$refs.containerRef!,
tipContainer: this.$refs.tipRef!,
spacesConfig: this.spacesConfig
})
// 设置全景自动旋转
this.vr360.controls.autoRotate = true
// 开始渲染全景
this.vr360.render()
// 实时监听页面尺寸变化,更新全景尺寸
this.vr360.listenResize()
// 当需要显示提示时
this.vr360.on('showTip', e => {
// 停止旋转场景
this.vr360!.controls.autoRotate = false
// 设置提示内容和提示容器位置
const {top, left, tip} = e
this.tip = {
top,
left,
title: tip.content.title,
content: tip.content.text,
show: true
}
})
// 当需要隐藏提示时
this.vr360.on('hideTip', () => {
// 重新开启旋转场景
this.vr360!.controls.autoRotate = true
// 隐藏提示容器
this.tip.show = false
})
},
destroy() {
// 页面卸载时注销全景实例
this.vr360?.destroy?.()
},
methods: {
// 切换全景空间
handleSwitchSpace(space: SpaceConfig) {
this.vr360?.switchSpace?.(space.id)
}
}
}
</script>
<style>
/* 引入自己的样式 */
@import './demo.css';
</style>
================================================
FILE: packages/doc/.vuepress/examples/firstHouse/vue3.vue
================================================
<template>
<div class="demo">
<!-- 提示 -->
<div
ref="tipRef"
class="demo-tip"
:style="{
transform: `translate(${tipLeft}px, ${tipTop + 50}px)`,
zIndex: showTip ? 99 : -1,
visibility: showTip ? 'visible' : 'hidden'
}"
>
<!-- 提示标题 -->
<div class="demo-tip-title">{{ tipTitle }}</div>
<!-- 提示内容 -->
<div class="demo-tip-content">{{ tipContent }}</div>
</div>
<!-- 360全景容器 -->
<div ref="containerRef" class="demo-container"></div>
<!-- 底部切换场景 -->
<div class="demo-bottom-bar">
<div
v-for="space in spacesConfig"
:key="space.id"
class="demo-bottom-card"
@click="handleSwitchSpace(space)"
:style="{
backgroundImage: `url(${space.cubeSpaceTextureUrls.left})`
}"
></div>
</div>
</div>
</template>
<script setup lang="ts">
import {onMounted, onUnmounted, ref} from 'vue'
import {Vr360} from '@nicepkg/vr360-core'
import type {SpaceConfig} from '@nicepkg/vr360-core'
const containerRef = ref<HTMLElement>() // 全景容器
const tipRef = ref<HTMLElement>() // 提示容器
const tipLeft = ref(0) // 提示容器的 left 或 translateX 值
const tipTop = ref(0) // 提示容器的 top 或 translateY 值
const showTip = ref(false) // 是否显示提示容器
const tipTitle = ref('') // 提示容器的标题
const tipContent = ref('') // 提示容器的内容
let vr360: InstanceType<typeof Vr360> // 全景实例
// 全景空间配置
const spacesConfig: SpaceConfig[] = [
{
id: 'spaceA', // 空间 id,用于切换空间,必须唯一
tips: [
// 提示,可选
{
id: '1', // 提示 id,用于缓存,在当前 tips 数组里要唯一,必须
position: {x: 0, y: -10, z: 40}, // 提示位置,必须
content: {
// 提示内容,在 showTip 事件会暴露,要包含什么你自己决定。
title: '豪华跑车',
text: '比奥迪还贵的豪华跑车'
}
},
{
id: '2',
// 自定义提示图标贴图,可选
textureUrl: 'https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png',
targetSpaceId: 'spaceB', // 提示点击后跳转的空间 id,可选
position: {x: -10, y: -4, z: 40},
content: {
title: '去客厅',
text: '一起去尊贵的客厅吧'
}
}
],
cubeSpaceTextureUrls: {
// 立方体贴图,分别为:背侧、下侧、前侧、左侧、右侧、上侧,必须
back: 'https://m.360buyimg.com/babel/jfs/t1/40814/31/19646/41953/63398297E0707fe35/4e831e60cf579899.jpg',
down: 'https://m.360buyimg.com/babel/jfs/t1/43941/23/19369/84038/633982d7E838acd9a/9e7a89cc910d3409.jpg',
front: 'https://m.360buyimg.com/babel/jfs/t1/150573/35/27827/113528/633982faE8556c0c0/b455284fd91885c6.jpg',
left: 'https://m.360buyimg.com/babel/jfs/t1/189204/25/29491/61430/63398310E3c180e43/26d9b459cf714f9f.jpg',
right: 'https://m.360buyimg.com/babel/jfs/t1/184948/34/28448/131232/63398323E61ff80fb/89ab84eda0421260.jpg',
up: 'https://m.360buyimg.com/babel/jfs/t1/86258/24/33602/70616/63398334Ea2dcf448/e2f5c275792fb3d6.jpg'
}
},
{
id: 'spaceB',
tips: [
{
id: '3',
position: {x: -2, y: -25, z: 40},
content: {
title: '香奈儿垃圾桶',
text: '里面装着主人不用的奢侈品'
}
},
{
id: '4',
position: {x: -20, y: 0, z: 40},
content: {
title: '宇宙牌冰箱',
text: '装着超级多零食'
}
},
{
id: '5',
textureUrl: 'https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png',
targetSpaceId: 'spaceA',
position: {
x: -8,
y: 0,
z: -40
},
content: {
title: '去门口',
text: '一起去门口吧'
}
}
],
cubeSpaceTextureUrls: {
back: 'https://m.360buyimg.com/babel/jfs/t1/48117/28/21445/120448/63398366Ede81497b/a46e362df5f7d0ed.jpg',
down: 'https://m.360buyimg.com/babel/jfs/t1/101209/24/26762/106253/63398376Eedb0db22/4f335c4ecd72ad74.jpg',
front: 'https://m.360buyimg.com/babel/jfs/t1/154056/6/26449/110652/63398388E8ecda044/22e1646534839a95.jpg',
left: 'https://m.360buyimg.com/babel/jfs/t1/198990/28/28201/74687/6339839aE28806a5e/43b311d3379397df.jpg',
right: 'https://m.360buyimg.com/babel/jfs/t1/209711/14/25233/92186/633983b0E8f4df687/750ba84061ea64a6.jpg',
up: 'https://m.360buyimg.com/babel/jfs/t1/186545/35/29054/29678/633983c2E72ef4848/92043b945a03fc29.jpg'
}
}
]
onMounted(() => {
// 初始化全景实例
vr360 = new Vr360({
container: containerRef.value!,
tipContainer: tipRef.value!,
spacesConfig
})
// 设置全景自动旋转
vr360.controls.autoRotate = true
// 开始渲染全景
vr360.render()
// 实时监听页面尺寸变化,更新全景尺寸
vr360.listenResize()
// 当需要显示提示时
vr360.on('showTip', e => {
// 停止旋转场景
vr360!.controls.autoRotate = false
// 设置提示内容和提示容器位置
const {top, left, tip} = e
showTip.value = true
tipLeft.value = left
tipTop.value = top
tipTitle.value = tip.content.title
tipContent.value = tip.content.text
})
// 当需要隐藏提示时
vr360.on('hideTip', () => {
// 重新开启旋转场景
vr360!.controls.autoRotate = true
// 隐藏提示容器
showTip.value = false
})
})
// 切换全景空间
function handleSwitchSpace(space: SpaceConfig) {
vr360.switchSpace(space.id)
}
onUnmounted(() => {
// 页面卸载时注销全景实例
vr360?.destroy?.()
})
</script>
<style>
/* 引入自己的样式 */
@import './demo.css';
</style>
================================================
FILE: packages/doc/.vuepress/index.build.html
================================================
<!DOCTYPE html>
<html lang="{{ lang }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="generator" content="VuePress {{ version }}">
<!--vuepress-ssr-head-->
<!--vuepress-ssr-resources-->
<!--vuepress-ssr-styles-->
</head>
<body>
<div id="vr-teleport"></div>
<div id="app"><!--vuepress-ssr-app--></div>
<!--vuepress-ssr-scripts-->
</body>
</html>
================================================
FILE: packages/doc/.vuepress/plugins/code-demo/CodeDemo.props.ts
================================================
import type {Project} from '@stackblitz/sdk'
import type {ExtractPropTypes, PropType} from 'vue'
export const getProps = () => ({
mode: {
type: String,
default: 'node'
},
project: {
type: Object as PropType<Project>
},
previewOnly: {
type: Boolean,
default: false
},
clickToLoad: {
type: Boolean,
default: false
},
openFile: {
type: String,
default: 'src/main.ts'
}
})
export type CodeDemoProps = Partial<ExtractPropTypes<ReturnType<typeof getProps>>>
================================================
FILE: packages/doc/.vuepress/plugins/code-demo/CodeDemo.vue
================================================
<template>
<div
ref="codeDemoRef"
class="code-demo"
:style="{
border: isFullScreen ? 'none' : '1px solid var(--c-border)',
borderRadius: isFullScreen ? '0px' : '8px'
}"
>
<div class="code-demo-top">
<div class="code-demo-title">{{ project?.title ?? DEFAULT_EDITOR_TITLE }}</div>
<div class="code-demo-fullscreen" @click="toggleFullScreen">
<ExitFullscreenIcon v-if="isFullScreen" />
<FullscreenIcon v-else />
</div>
</div>
<div ref="codeDemoEditorRef" class="code-demo-editor"></div>
</div>
</template>
<script setup lang="ts">
import {onMounted, ref} from 'vue'
import sdk from '@stackblitz/sdk'
import type {ProjectFiles, Project} from '@stackblitz/sdk'
import {getProps} from './CodeDemo.props'
import {DEFAULT_EDITOR_TITLE} from './constant'
import {ExitFullscreenIcon, FullscreenIcon} from './Icons'
const props = defineProps(getProps())
const codeDemoRef = ref<HTMLElement>()
const codeDemoEditorRef = ref<HTMLIFrameElement>()
const isFullScreen = ref(false)
const toggleFullScreen = () => {
if (!codeDemoRef.value) return
if (isFullScreen.value) {
document.exitFullscreen()
isFullScreen.value = false
} else {
codeDemoRef.value.requestFullscreen()
isFullScreen.value = true
}
}
onMounted(async () => {
if (!codeDemoEditorRef.value || !props.project) return
codeDemoEditorRef.value.setAttribute('frameborder', '0')
try {
// eslint-disable-next-line no-restricted-globals
const defaultFiles: ProjectFiles = (await self.loadCodeDemoModeDefaultFiles?.(props.mode)) ?? {}
const finalProject = {
...props.project,
files: {
...defaultFiles,
...props.project.files
}
} as Project
await sdk.embedProject(codeDemoEditorRef.value, finalProject, {
forceEmbedLayout: true,
openFile: props.openFile,
hideNavigation: true,
height: '100%',
view: props.previewOnly ? 'preview' : undefined,
clickToLoad: props.clickToLoad
})
} catch (error) {
console.error('CodeDemoError:', error)
}
})
</script>
<style>
.code-demo {
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
background-color: #2e3138;
}
.code-demo-top {
box-sizing: border-box;
display: flex;
flex-shrink: 0;
align-items: center;
width: 100%;
height: 2rem;
padding: 0 0.5em;
overflow: hidden;
font-size: 14px;
color: #ccc;
}
.code-demo-title {
flex: 1;
}
.code-demo-fullscreen {
flex-shrink: 0;
font-size: 20px;
cursor: pointer;
}
.code-demo-editor {
box-sizing: border-box;
display: block;
height: 100%;
min-height: 500px;
border: none;
}
</style>
================================================
FILE: packages/doc/.vuepress/plugins/code-demo/Icons.tsx
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import type {VNode} from 'vue'
import {defineComponent, h} from 'vue'
const createComponent = (name: string, render: (props: Record<string, any>) => VNode) =>
defineComponent({
name,
render() {
const props = {...this.$props, ...this.$attrs}
return render(props)
}
})
export const FullscreenIcon = createComponent('FullscreenIcon', props => {
//
// <svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
// <path d="M5 5h5v2H7v3H5V5m9 0h5v5h-2V7h-3V5m3 9h2v5h-5v-2h3v-3m-7 3v2H5v-5h2v3h3z" fill="currentColor"></path>
// </svg>
//
return h(
'svg',
{
width: '1em',
height: '1em',
viewBox: '0 0 24 24',
...props
},
[
h('path', {
d: 'M5 5h5v2H7v3H5V5m9 0h5v5h-2V7h-3V5m3 9h2v5h-5v-2h3v-3m-7 3v2H5v-5h2v3h3z',
fill: 'currentColor'
})
]
)
})
export const ExitFullscreenIcon = createComponent('ExitFullscreenIcon', props => {
// <svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
// <path d="M14 14h5v2h-3v3h-2v-5m-9 0h5v5H8v-3H5v-2m3-9h2v5H5V8h3V5m11 3v2h-5V5h2v3h3z" fill="currentColor"></path>
// </svg>
return h(
'svg',
{
width: '1em',
height: '1em',
viewBox: '0 0 24 24',
...props
},
[
h('path', {
d: 'M14 14h5v2h-3v3h-2v-5m-9 0h5v5H8v-3H5v-2m3-9h2v5H5V8h3V5m11 3v2h-5V5h2v3h3z',
fill: 'currentColor'
})
]
)
})
================================================
FILE: packages/doc/.vuepress/plugins/code-demo/clientConfigFile.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import type {CodeDemoProps} from './CodeDemo.props'
import {defineClientConfig} from '@vuepress/client'
import type {ConcreteComponent, DefineComponent} from 'vue'
import {h, resolveComponent} from 'vue'
import * as base64 from 'js-base64'
export default defineClientConfig({
async enhance({app}) {
// eslint-disable-next-line @typescript-eslint/ban-types
let CodeDemo: DefineComponent<{}, {}, any> | undefined
if (!__VUEPRESS_SSR__) {
// eslint-disable-next-line unicorn/no-await-expression-member
CodeDemo = (await import('./CodeDemo.vue')).default
// set global css
const styleEl = document.createElement('style')
styleEl.innerHTML = `
.code-demo-wrapper {
margin-top: 1rem;
margin-bottom: 1rem;
}
.code-demo-wrapper + .code-demo-wrapper {
margin-top: 3rem;
}
`
document.head.append(styleEl)
}
if (CodeDemo) {
app.component('CodeDemo', CodeDemo)
}
// wrap the component with default options
app.component('CodeDemoClient', (defaultProps: CodeDemoProps) => {
if (!CodeDemo) return null
const ClientOnly = resolveComponent('ClientOnly')
// eslint-disable-next-line no-restricted-globals
const codeDemoOptions = self.loadCodeDemoOptions?.(defaultProps) ?? defaultProps
return h(ClientOnly, {}, () =>
h(CodeDemo as ConcreteComponent, {
...codeDemoOptions
})
)
})
app.config.globalProperties.base64 = base64 // decode the options in sandbox component
}
})
================================================
FILE: packages/doc/.vuepress/plugins/code-demo/constant.ts
================================================
// 用于 Stackblitz 示例的默认标题(未覆盖时)
export const DEFAULT_EDITOR_TITLE = 'Vr360 Example'
// 用于 Stackblitz 示例的默认描述(未覆盖时)
export const DEFAULT_EDITOR_DESCRIPTION = ''
// 用于所有 @nicepkg/vr360-*包的默认包版本。
export const DEFAULT_VR360_VERSION = '^6.0.0'
================================================
FILE: packages/doc/.vuepress/plugins/code-demo/global.d.ts
================================================
import type {ProjectFiles} from '@stackblitz/sdk'
import type {CodeDemoProps} from './CodeDemo.props'
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface Window {
loadCodeDemoOptions?: (preOptions: CodeDemoProps) => CodeDemoProps
loadCodeDemoModeDefaultFiles?: (mode: string) => ProjectFiles | Promise<ProjectFiles>
}
}
================================================
FILE: packages/doc/.vuepress/plugins/code-demo/index.ts
================================================
export * from './plugin'
================================================
FILE: packages/doc/.vuepress/plugins/code-demo/plugin.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import path from 'node:path'
import fs from 'node:fs'
import type {Plugin} from '@vuepress/core'
import markdownItContainer from 'markdown-it-container'
import type {MarkdownEnv} from '@vuepress/markdown'
import type * as Token from 'markdown-it/lib/token'
import type * as Renderer from 'markdown-it/lib/renderer'
import type {CodeDemoProps} from './CodeDemo.props'
import type {ProjectFiles} from '@stackblitz/sdk'
import * as base64 from 'js-base64'
import {DEFAULT_EDITOR_DESCRIPTION, DEFAULT_EDITOR_TITLE} from './constant'
const pathResolve = (..._path: string[]) => path.resolve(__dirname, ..._path)
export type MarkdownItRenderFn = (
tokens: Token[],
index: number,
options: any,
env: MarkdownEnv,
self: Renderer
) => string
export type CodeDemoOptions = {
codeDemoMark?: string
clickToLoad?: boolean
resolvePath?: (filePath: string) => string
}
export type RenderPlaceFunction = (description: string, codeBlockTokens?: Token[]) => string
export const codeDemoPlugin = (options: CodeDemoOptions = {}): Plugin => {
const {codeDemoMark = 'demo', clickToLoad = false, resolvePath = p => p} = options
// const START_TYPE = `container_${codeDemoMark}_open`
const END_TYPE = `container_${codeDemoMark}_close`
const CODE_BLOCK_TYPE = 'fence'
const START_NESTING = 1
// const END_NESTING = -1
const renderBefore: RenderPlaceFunction = (des, codeBlockTokens) => {
const files: ProjectFiles = {}
let openFile: string | undefined
const [mode, title, description] = des.split('--').map(s => s.trim())
if (codeBlockTokens && codeBlockTokens.length > 0) {
codeBlockTokens.map(token => {
const [lang, filename] = token.info.split(/\s+/)
// 找出代码内容需要插入的部分 /*# 路径 #*/
const importPathReg = /\/\*#\s*(.+)\s*#\*\//g
// 代码内容
const codeContent = token.content.replace(importPathReg, (match, p: string) => {
const currentImportPath = pathResolve(resolvePath(p.trim()))
if (!fs.existsSync(currentImportPath)) return match
const importCode = fs.readFileSync(currentImportPath, 'utf8')
return importCode || match
})
// 最终文件名
const _filename = filename.endsWith(`.${lang}`) ? filename : `${filename}.${lang}`
if (!openFile) openFile = _filename
files[_filename] = codeContent
})
}
const options: CodeDemoProps = {
mode,
openFile,
clickToLoad,
project: {
template: 'node',
title: title || DEFAULT_EDITOR_TITLE,
description: description || DEFAULT_EDITOR_DESCRIPTION,
files
}
}
const optionsBase64 = base64.encode(JSON.stringify(options))
return `<div class="code-demo-wrapper ${codeDemoMark}"><code-demo-client v-bind="JSON.parse(base64.decode('${optionsBase64}'))">\n`
}
const renderAfter: RenderPlaceFunction = () => '</code-demo-client></div>\n'
const descriptionsStack: string[] = []
const render: MarkdownItRenderFn = (tokens, index, opts, env) => {
const token = tokens[index]
if (token.nesting === START_NESTING) {
// `before` tag
// resolve description (title)
const description = token.info.trim().slice(codeDemoMark.length).trim() || DEFAULT_EDITOR_TITLE
descriptionsStack.push(description)
let i = index + 1
const codeBlockTokens: Token[] = []
while (tokens[i].type !== END_TYPE) {
const nextToken = tokens[i]
if (nextToken.type === CODE_BLOCK_TYPE) {
codeBlockTokens.push(nextToken)
}
i++
}
return renderBefore(description, codeBlockTokens)
} else {
// `after` tag
// pop the description from stack
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const description = descriptionsStack.pop() || ''
// render
return renderAfter(description)
}
}
const plugin: Plugin = {
name: 'vuepress-plugin-code-demo',
clientConfigFile: pathResolve('./clientConfigFile.ts').replace(/\\/g, '/'),
extendsMarkdown: md => {
md.use(markdownItContainer, codeDemoMark, {render})
}
}
return plugin
}
================================================
FILE: packages/doc/.vuepress/plugins/index.ts
================================================
import type {PluginConfig} from 'vuepress'
import {registerComponentsPlugin} from '@vuepress/plugin-register-components'
import {googleAnalyticsPlugin} from '@vuepress/plugin-google-analytics'
import {shikiPlugin} from '@vuepress/plugin-shiki'
import {path} from '@vuepress/utils'
import {isProd} from '../utils/common'
import {codeDemoPlugin} from './code-demo'
const pathResolve = (..._path: string[]) => path.resolve(__dirname, ..._path)
const vuepressPlugins: PluginConfig = [
// for google search
googleAnalyticsPlugin({
id: ''
}),
// auto register globally components
registerComponentsPlugin({
componentsDir: pathResolve('../components')
}),
codeDemoPlugin({
clickToLoad: true,
resolvePath: str => str.replace(/^@/, pathResolve('../'))
})
]
if (isProd) {
vuepressPlugins.push(
// code highlighting
shikiPlugin({
theme: 'dark-plus'
})
)
}
export const plugins = vuepressPlugins
================================================
FILE: packages/doc/.vuepress/public/browserconfig.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/images/icons/mstile-150x150.png"/>
<TileColor>#0c4dc4</TileColor>
</tile>
</msapplication>
</browserconfig>
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/demo.css.txt
================================================
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
.demo {
position: relative;
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
overflow: hidden;
}
.demo-tip {
position: absolute;
top: 0;
left: 0;
z-index: -1;
display: flex;
flex-direction: column;
justify-content: center;
width: 240px;
height: 60px;
padding: 4px;
color: #fff;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 4px;
}
.demo-tip-title {
font-weight: bold;
}
.demo-container {
width: 100%;
height: 100%;
}
.demo-bottom-bar {
display: flex;
align-items: center;
width: 100%;
height: 100px;
}
.demo-bottom-card {
width: 140px;
height: 70px;
margin-left: 1rem;
cursor: pointer;
background-repeat: no-repeat;
background-size: cover;
border-radius: 4px;
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/html/package.json.txt
================================================
{
"name": "playground-html",
"private": true,
"scripts": {
"dev": "http-server ./ -p 8000 -o"
},
"devDependencies": {
"http-server": "^14.1.1"
}
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/react/index.html.txt
================================================
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" />
<title>React App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<script>
(function () {
// ios safari 禁止缩放
document.addEventListener('touchmove', function(event) {
event = event.originalEvent || event;
if(event.scale !== undefined && event.scale !== 1) {
event.preventDefault();
}
}, false);
})()
</script>
</head>
<body class="font-sans">
<noscript>
<strong>We're sorry but web-cli doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/react/package.json.txt
================================================
{
"name": "playground-react",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"@nicepkg/vr360-core": "latest",
"clsx": "^1.2.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"three": "^0.145.0"
},
"devDependencies": {
"@types/node": "^18.7.23",
"@types/react": "18.0.21",
"@types/react-dom": "18.0.6",
"@types/three": "^0.144.0",
"@vitejs/plugin-react": "^2.1.0",
"typescript": "4.7.4",
"vite": "3.0.9"
}
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/react/src/main.tsx.txt
================================================
import React from 'react'
import ReactDOM from 'react-dom'
import Example from './Example'
ReactDOM.render(
<React.StrictMode>
<Example />
</React.StrictMode>,
document.querySelector('#app')
)
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/react/tsconfig.json.txt
================================================
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist",
"target": "esnext",
"composite": true,
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "react-jsx",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"skipLibCheck": true,
"types": ["vite/client"],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*", "types/**/*", "*.ts", "*.js"]
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/react/vite.config.ts.txt
================================================
import {defineConfig} from 'vite'
import path from 'node:path'
import viteReact from '@vitejs/plugin-react'
const pathResolve = (..._path: string[]) => path.resolve(__dirname, ..._path)
export default defineConfig({
resolve: {
alias: {
'@/': `${pathResolve('./src')}/`
}
},
plugins: [
viteReact()
]
})
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue2/index.html.txt
================================================
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" />
<title>Vue2 App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<script>
(function () {
// ios safari 禁止缩放
document.addEventListener('touchmove', function(event) {
event = event.originalEvent || event;
if(event.scale !== undefined && event.scale !== 1) {
event.preventDefault();
}
}, false);
})()
</script>
</head>
<body class="font-sans">
<noscript>
<strong>We're sorry but web-cli doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue2/package.json.txt
================================================
{
"name": "playground-vue2",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"@nicepkg/vr360-core": "latest",
"three": "^0.145.0",
"vue": "2.6.14"
},
"devDependencies": {
"@types/node": "18.7.23",
"@types/three": "^0.144.0",
"@vue/runtime-dom": "latest",
"typescript": "4.7.4",
"vite": "^2.9.9",
"vite-plugin-vue2": "^2.0.2",
"vue-template-compiler": "2.6.14"
}
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue2/src/App.vue.txt
================================================
<template>
<Example></Example>
</template>
<script lang="ts">
import Example from './Example.vue'
export default {
name: 'App',
components: {
Example
}
}
</script>
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue2/src/main.ts.txt
================================================
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App)
}).$mount('#app')
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue2/tsconfig.json.txt
================================================
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist",
"target": "esnext",
"composite": true,
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"skipLibCheck": true,
"types": ["vite/client"],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*", "types/**/*", "*.ts", "*.js"]
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue2/types/module.d.ts.txt
================================================
declare module '*.vue' {
import type {VueConstructor} from 'vue'
const component: VueConstructor
export default component
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue2/vite.config.ts.txt
================================================
import path from 'node:path'
import {defineConfig} from 'vite'
import {createVuePlugin} from 'vite-plugin-vue2'
const pathResolve = (...args: string[]) => path.resolve(__dirname, ...args)
export default defineConfig({
resolve: {
alias: {
'@/': `${pathResolve('./src')}/`
}
},
plugins: [
createVuePlugin({
jsx: true,
jsxOptions: {
compositionAPI: true
}
})
]
})
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue3/index.html.txt
================================================
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" />
<title>Vue3 App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<script>
(function () {
// ios safari 禁止缩放
document.addEventListener('touchmove', function(event) {
event = event.originalEvent || event;
if(event.scale !== undefined && event.scale !== 1) {
event.preventDefault();
}
}, false);
})()
</script>
</head>
<body class="font-sans">
<noscript>
<strong>We're sorry but web-cli doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue3/package.json.txt
================================================
{
"name": "playground-vue3",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"@nicepkg/vr360-core": "latest",
"three": "^0.145.0",
"vue": "^3.2.40"
},
"devDependencies": {
"@types/node": "18.7.23",
"@types/three": "^0.144.0",
"@vitejs/plugin-vue": "^3.1.0",
"@vitejs/plugin-vue-jsx": "^2.0.1",
"typescript": "4.7.4",
"vite": "3.0.9"
}
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue3/src/App.vue.txt
================================================
<template>
<Example></Example>
</template>
<script setup lang="ts">
import Example from './Example.vue'
</script>
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue3/src/main.ts.txt
================================================
import {createApp} from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
app.config.globalProperties.productionTip = false
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue3/tsconfig.json.txt
================================================
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist",
"target": "esnext",
"composite": true,
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"skipLibCheck": true,
"types": ["vite/client"],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*", "types/**/*", "*.ts", "*.js"]
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue3/types/module.d.ts.txt
================================================
declare module '*.vue' {
import type {DefineComponent} from 'vue-demi'
const Component: DefineComponent<{}, {}, any>
export default Component
}
================================================
FILE: packages/doc/.vuepress/public/code-demo-templates/vue3/vite.config.ts.txt
================================================
import path from 'node:path'
import {defineConfig} from 'vite'
import Vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
const pathResolve = (...args: string[]) => path.resolve(__dirname, ...args)
export default defineConfig({
resolve: {
alias: {
'@/': `${pathResolve('./src')}/`
}
},
plugins: [
Vue({
include: [/\.vue$/]
}),
vueJsx()
]
})
================================================
FILE: packages/doc/.vuepress/public/manifest.webmanifest
================================================
{
"name": "vr360",
"short_name": "vr360",
"description": "快速实现你的全景开发需求",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "/images/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "/images/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
]
}
================================================
FILE: packages/doc/.vuepress/styles/index.scss
================================================
:root {
// brand colors
--c-brand: #0c4dc4;
--c-brand-light: #0b3788;
scroll-behavior: smooth;
}
html.dark {
// brand colors
--c-brand: #1fa8f8;
--c-brand-light: #3bb3f8;
}
.sidebar-item.collapsible {
cursor: pointer;
}
.site-name.can-hide {
display: none;
}
body {
position: relative;
width: 100vw;
min-height: 100vh;
overflow-x: hidden;
overflow-y: auto;
background-color: var(--c-bg);
}
#app {
position: absolute;
top: 0;
left: 0;
z-index: 99;
min-width: 100vw;
min-height: 100vh;
}
.code-group {
width: 100%;
max-width: calc(100vw - 3rem);
}
.theme-default-content div[class*='language-'] {
width: calc(100% + 3rem);
max-width: 100vw;
}
.theme-default-content .code-group div[class*='language-'] {
width: auto;
max-width: auto;
}
================================================
FILE: packages/doc/.vuepress/theme/components/Home.vue
================================================
<script setup lang="ts">
import HomeContent from '@vuepress/theme-default/components/HomeContent.vue'
import HomeFeatures from './HomeFeatures.vue'
import HomeFooter from '@vuepress/theme-default/components/HomeFooter.vue'
import HomeHero from '@vuepress/theme-default/components/HomeHero.vue'
import HomeVrBg from './HomeVrBg.vue'
</script>
<template>
<main class="home">
<HomeHero />
<HomeFeatures />
<HomeContent />
<HomeFooter />
<ClientOnly>
<teleport to="#vr-teleport">
<HomeVrBg :show-mask="true"></HomeVrBg>
</teleport>
</ClientOnly>
</main>
</template>
================================================
FILE: packages/doc/.vuepress/theme/components/HomeFeatures.vue
================================================
<script setup lang="ts">
import {usePageFrontmatter} from '@vuepress/client'
import {isArray} from '@vuepress/shared'
import type {DefaultThemeHomePageFrontmatter} from '@vuepress/theme-default'
import {computed} from 'vue'
import AutoLink from '@vuepress/theme-default/components/AutoLink.vue'
type HomePageFormatter = {
features?: {
title: string
details: string
link: string
disabled: boolean
}[]
} & Omit<DefaultThemeHomePageFrontmatter, 'features'>
// eslint-disable-next-line react-hooks/rules-of-hooks
const frontmatter = usePageFrontmatter<HomePageFormatter>()
const features = computed(() => {
if (isArray(frontmatter.value.features)) {
return frontmatter.value.features
}
return []
})
</script>
<template>
<div v-if="features.length > 0" class="features">
<div
v-for="feature in features"
:key="feature.title"
class="feature"
:style="{
cursor: feature.disabled ? 'not-allowed' : 'pointer',
opacity: feature.disabled ? 0.8 : 1
}"
>
<h4>
<AutoLink
v-if="!feature.disabled"
:item="{
text: feature.title,
link: feature.link || './'
}"
>
{{ feature.title }}
</AutoLink>
<div v-else>
{{ feature.title }}
</div>
</h4>
<p>{{ feature.details }}</p>
</div>
</div>
</template>
================================================
FILE: packages/doc/.vuepress/theme/components/HomeVrBg.vue
================================================
<template>
<div class="vr-container-wrapper" :style="{height: height + 'px'}">
<div
ref="tipRef"
class="vr-tip"
:style="{
left: tipLeft + 'px',
top: tipTop + 50 + 'px',
zIndex: showTip ? 99 : -1
}"
>
<div class="vr-tip-title">{{ tipTitle }}</div>
<div class="vr-tip-content">{{ tipContent }}</div>
</div>
<div ref="containerRef" class="vr-container"></div>
<div v-if="showMask" class="vr-mask"></div>
</div>
</template>
<script setup lang="ts">
/* eslint-disable import/no-named-as-default-member */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import {onMounted, ref, watch, nextTick, onUnmounted} from 'vue'
import type {SpaceConfig} from '@nicepkg/vr360-core'
import {Vr360} from '@nicepkg/vr360-core'
import {useElementSize} from '@vueuse/core'
import textures from '../../../../../textures.json'
defineProps({
showMask: {
type: Boolean,
default: false
}
})
const appRef = ref<HTMLElement>()
const containerRef = ref<HTMLElement>()
const tipRef = ref<HTMLElement>()
const tipLeft = ref(0)
const tipTop = ref(0)
const showTip = ref(false)
const tipTitle = ref('')
const tipContent = ref('')
let vr360: InstanceType<typeof Vr360>
const spacesConfig = ref<SpaceConfig[]>([
{
id: 'spaceA',
camera: {
position: {
x: 0,
y: 0,
z: 0
}
},
tips: [],
cubeSpaceTextureUrls: textures.beijing
}
])
onMounted(() => {
vr360 = new Vr360({
container: containerRef.value!,
tipContainer: tipRef.value!,
spacesConfig: spacesConfig.value
})
vr360.controls.autoRotate = true
vr360.controls.autoRotateSpeed = 0.2
vr360.render()
vr360.listenResize()
vr360.on('showTip', e => {
vr360!.controls.autoRotate = false
const {top, left, tip} = e
showTip.value = true
tipLeft.value = left
tipTop.value = top
tipTitle.value = tip.content.title
tipContent.value = tip.content.text
})
vr360.on('hideTip', () => {
vr360!.controls.autoRotate = true
showTip.value = false
})
appRef.value = document.querySelector<HTMLElement>('#app')!
})
onUnmounted(() => {
vr360.destroy()
})
// eslint-disable-next-line react-hooks/rules-of-hooks
const {height} = useElementSize(appRef)
watch(height, async () => {
await nextTick()
vr360.updateContainerSize()
})
</script>
<style scoped>
.vr-container-wrapper {
position: absolute;
top: 0;
left: 0;
z-index: 1;
display: flex;
flex-direction: column;
width: 100vw;
min-height: 100vh;
overflow: hidden;
}
.vr-tip {
position: absolute;
z-index: 99;
display: flex;
flex-direction: column;
justify-content: center;
width: 240px;
height: 60px;
padding: 1rem;
color: #fff;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.5);
}
.vr-tip-title {
font-weight: bold;
}
.vr-container {
width: 100%;
height: 100%;
/* 修复 edge canvas底部空白,我也不知道为什么,但是有效 */
border: 0.5px solid;
}
.vr-container :deep(canvas) {
background-color: #fff;
}
.vr-mask {
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 1) 60%,
rgba(255, 255, 255, 0.8) 90%,
rgba(255, 255, 255, 0.2)
);
}
html.dark .vr-mask {
background: linear-gradient(to bottom, rgba(34, 39, 46, 1) 60%, rgba(34, 39, 46, 0.8) 90%, rgba(34, 39, 46, 0.2));
}
</style>
================================================
FILE: packages/doc/.vuepress/theme/index.ts
================================================
import type {Theme} from '@vuepress/core'
import {defaultTheme} from '@vuepress/theme-default'
import {path} from '@vuepress/utils'
import {navbar, sidebar} from '../configs'
import {isProd} from '../utils/common'
const pathResolve = (..._path: string[]) => path.resolve(__dirname, ..._path)
export const localTheme = (): Theme => {
return {
name: 'vuepress-theme-local',
extends: defaultTheme({
logo: '/images/logo.png',
logoDark: '/images/logo-dark.png',
repo: 'nicepkg/vr360',
docsBranch: 'master',
docsDir: 'packages/doc',
// theme-level locales config
locales: {
/**
* Chinese locale config
*/
'/': {
// navbar
navbar: navbar.zh,
selectLanguageName: '简体中文',
selectLanguageText: '选择语言',
selectLanguageAriaLabel: '选择语言',
// sidebar
sidebar: sidebar.zh,
// page meta
editLinkText: '在 GitHub 上编辑此页',
lastUpdatedText: '上次更新',
contributorsText: '贡献者',
// custom containers
tip: '提示',
warning: '注意',
danger: '警告',
// 404 page
notFound: ['这里什么都没有', '我们怎么到这来了?', '这是一个 404 页面', '看起来我们进入了错误的链接'],
backToHome: '返回首页',
// a11y
openInNewWindow: '在新窗口打开',
toggleColorMode: '切换主题',
toggleSidebar: '切换侧边栏'
},
/**
* English locale config
*
* As the default locale of @vuepress/theme-default is English,
* we don't need to set all of the locale fields
*/
'/en/': {
// navbar
navbar: navbar.en,
// sidebar
sidebar: sidebar.en,
// page meta
editLinkText: 'Edit this page on GitHub'
}
},
themePlugins: {
// only enable git plugin in production mode
git: isProd,
// use shiki plugin in production mode instead
prismjs: !isProd,
// disable the @vuepress/plugin-nprogress plugin to fix the bug of `Cannot set properties of undefined (setting 'NProgress')`
nprogress: false
}
}),
alias: {
'@theme/Home.vue': pathResolve('components/Home.vue'),
'@theme/HomeFeatures.vue': pathResolve('components/HomeFeatures.vue')
}
}
}
================================================
FILE: packages/doc/.vuepress/types/module.d.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
declare module '*.vue' {
import type {DefineComponent} from 'vue'
// eslint-disable-next-line @typescript-eslint/ban-types
const Component: DefineComponent<{}, {}, any>
export default Component
}
================================================
FILE: packages/doc/.vuepress/utils/common.ts
================================================
import rootPkg from '../../../../package.json'
export const version = rootPkg.version as string
export const isProd = process.env.NODE_ENV === 'production'
================================================
FILE: packages/doc/CHANGELOG.md
================================================
# 0.3.0 (2022-10-08)
### Bug Fixes
- **@nicepkg/vr360-core:** 修复移动端的提示移动 bug ([7090a98](https://github.com/nicepkg/vr360/commit/7090a9805bc472240de6ad4467bbadc14fd6151d))
# 0.3.0 (2022-10-02)
## 0.2.1 (2022-10-02)
# 0.2.0 (2022-10-02)
### Bug Fixes
- 修复 vr360-core tips 在滑动时的 bug ([e179ebc](https://github.com/nicepkg/vr360/commit/e179ebc2697314bc455320eecf8beb6182a53ded))
### Features
- 暴露更多的事件,设置默认自动旋转 ([4e21c53](https://github.com/nicepkg/vr360/commit/4e21c53ac945532020a7fbbfa46644294c33b49d))
- 初始化项目 ([06f39d1](https://github.com/nicepkg/vr360/commit/06f39d141004a1d0b1a125ad598298baf15ffee8))
- 添加标签提示和空间切换功能 ([6aa67d3](https://github.com/nicepkg/vr360/commit/6aa67d39113b06d05036ecc1d66ab1d70a2f4cf5))
- 添加如视 vr 导入支持 ([5a9eb5d](https://github.com/nicepkg/vr360/commit/5a9eb5d7a33d092be8cea5565490f268376d2f79))
- 文档添加示例插件、core 的纹理 top、bottom 改为 up 和 down ([70ccb2c](https://github.com/nicepkg/vr360/commit/70ccb2c40a06079ffb3cf50b27f986ab19b1f7db))
- 修改 package.json 说明 ([e5e4ad0](https://github.com/nicepkg/vr360/commit/e5e4ad04b1c7a9cddff3af6f73d438fd1de85b25))
- 优化代码 ([4519c4a](https://github.com/nicepkg/vr360/commit/4519c4a0fb230bb62bed49f97e0824c8977be3ca))
- 优化 sdk,添加纹理缓存预加载 ([9dd57e7](https://github.com/nicepkg/vr360/commit/9dd57e71b8f5a38ecc9901395a9b189481172edb))
- 重写核心,添加最小化更新配置支持 ([289091b](https://github.com/nicepkg/vr360/commit/289091b13dfe0495b44ae9e5353b78d2712e9762))
================================================
FILE: packages/doc/README.md
================================================
---
home: true
title: 首页
heroImage: /images/logo.png
heroImageDark: /images/logo-dark.png
heroText: null
tagline: 快速实现你的全景开发需求
actions:
- text: 快速上手
link: /guide/
type: primary
- text: Github
link: https://github.com/nicepkg/vr360
type: secondary
features:
- title: vr360-core
details: json 驱动的全景浏览库,设计框架无关性,可用于任何框架,如vue/react/angular/svelte/solidjs...
link: '/libs/vr360-core/README.md'
- title: vr360-ui
details: (开发中...)提供一个现成的 vr360 viewer 和 editor 组件,基于 stencil 构建的 web component。
disabled: true
link: ''
- title: vr360-ui-vue2
details: (开发中...)vr360-ui 的 vue2 二次封装版本,开箱即用。
disabled: true
link: ''
- title: vr360-ui-vue3
details: (开发中...)vr360-ui 的 vue3 二次封装版本,开箱即用。
disabled: true
link: ''
- title: vr360-ui-react
details: (开发中...)vr360-ui 的 react 二次封装版本,开箱即用。
disabled: true
link: ''
- title:
details:
footer: MIT License | Copyright © 2022-present YangJinMing
---
================================================
FILE: packages/doc/bundler.config.ts
================================================
import {path} from '@vuepress/utils'
import type {ViteBundlerOptions} from 'vuepress'
const pathResolve = (..._path: string[]) => path.resolve(__dirname, ..._path)
export const bundlerConfig = {
viteOptions: {
resolve: {
alias: [
{
find: /@(?=\/)/,
replacement: pathResolve('./.vuepress')
}
]
},
build: {
chunkSizeWarningLimit: Number.POSITIVE_INFINITY
}
}
} as ViteBundlerOptions
================================================
FILE: packages/doc/guide/README.md
================================================
# 介绍
Vr360 是一个基于 threejs 能让你快速实现业务全景需求的库,比如全景看房、全景街景、全景景点。
它的核心被设计为框架无关性,可以用 json 配置的方式快速实现常见全景需求。
后续还会提供高度封装的 viewer 和 editor 等组件,很适合懒人,会提供 vue2/3 、 react 、web component 版本。
json 驱动视图的特性是好维护,你甚至可以不用接触 threejs。写出来的代码拉条哈士奇过来也能维护。
## 库列表
#### 核心 [vr360-core](/libs/vr360-core/)
vr360 核心库,提供了基础的全景浏览功能,你可以用它来快速实现你的全景需求。
#### web 组件 [vr360-ui](/libs/vr360-ui/)
提供一个现成的 vr360 viewer 和 editor 组件,基于 stencil 构建的 web component。(开发中...)
#### vue2 组件 [vr360-ui-vue2](/libs/vr360-ui-vue2/)
vr360-ui 的 vue2 二次封装版本,开箱即用。(开发中...)
#### vue3 组件 [vr360-ui-vue3](/libs/vr360-ui-vue2/)
vr360-ui 的 vue2 二次封装版本,开箱即用。(开发中...)
#### react 组件 [vr360-ui-react](/libs/vr360-ui-react/)
vr360-ui 的 react 二次封装版本,开箱即用。(开发中...)
================================================
FILE: packages/doc/guide/questions.md
================================================
# 常见问题
## vr360-ui 为什么没有 angular 版
vr360-ui 是基于 stencil.js 开发,适配 angular 是很容易的,但是由于本人没怎么用过 angular,适配完也不知道有没有 bug,所以欢迎提 pr 。
================================================
FILE: packages/doc/libs/vr360-core/README.md
================================================
# 介绍
`@nicepkg/vr360-core` 是一个基于 [threejs](https://github.com/mrdoob/three.js/) 的全景库,非常适合用来做全景看房、全景街景、全景景点等业务需求。
它支持 json 配置驱动视图,你可以用 json 配置的方式快速实现常见全景需求。
## 特性
- json 驱动视图,快速实现全景业务需求
- 高性能,支持自动找出 json 变更的部分,最小化更新到 3d 全景里,不会整个场景重建。
- 支持增删改提示点,hover 提示点的弹窗支持自定义定制,支持自定义提示点的贴图。
- 内置场景切换穿梭动画
- 暴露 scene、camera、renderer 等 [threejs](https://github.com/mrdoob/three.js/) 对象,方便你更高的定制化
- 基于事件驱动的数据交互,设计框架无关性
- 完整的 ts 支持
## 安装
:::: code-group
::: code-group-item npm
```bash
npm i @nicepkg/vr360-core threejs
```
:::
::: code-group-item yarn
```bash
yarn add @nicepkg/vr360-core threejs
```
:::
::: code-group-item pnpm
```bash
pnpm add @nicepkg/vr360-core threejs
```
:::
::::
## 浏览器(CDN)
:::: code-group
::: code-group-item Unpkg
```html:no-v-pre
<!-- 引入 threejs -->
<script src="https://unpkg.com/three@0.145.0/build/three.min.js"></script>
<!-- 引入 vr360-core -->
<script src="https://unpkg.com/@nicepkg/vr360-core@{{version}}"></script>
<script>
// 使用
const {Vr360} = Vr360Core // 原生使用时,所有的导出都在 window.Vr360Core 里
// 初始化全景实例
const vr360 = new Vr360({...})
// 开始渲染全景
vr360.render()
</script>
```
:::
::: code-group-item JsDelivr
```html:no-v-pre
<!-- 引入 threejs -->
<script src="https://cdn.jsdelivr.net/npm/three@0.145.0/build/three.min.js"></script>
<!-- 引入 vr360-core -->
<script src="https://cdn.jsdelivr.net/npm/@nicepkg/vr360-core@{{version}}"></script>
<script>
// 使用
const {Vr360} = Vr360Core // 原生使用时,所有的导出都在 window.Vr360Core 里
// 初始化全景实例
const vr360 = new Vr360({...})
// 开始渲染全景
vr360.render()
</script>
```
:::
::::
## 为什么
如果产品叫你实现一个类似全景看房的功能,而且时间紧,你会怎么做?
学 threejs 知识,然后徒手撸出全景,然后自己抽象出一个 json 配置驱动和后端数据联调对接?
可能还要自己写一个全景编辑器?
可以但没必要,你完全可以把时间省下来做些有意义的事,你只要用 `@nicepkg/vr360-core` 就可以了。
简单点的业务需求仅需 json 配置就能实现,重要是这种代码拉条哈士奇也能维护。
编辑器还在开发中,复杂度不会消失,只会转移,当你岁月静好,一定是作者在为你负重前行。
## 使用
请阅读我们的 [属性文档](./properties.md)、 [函数文档](./methods.md)、[事件文档](./events.md),了解如何使用 `@nicepkg/vr360-core`。
除了下面的简易示例,你也可以浏览我们的 [完整项目示例](./example.md)
#### 简易示例效果
<br>
<DemoA></DemoA>
#### 简易示例代码(cv 专用)
<br>
<CodeGroup>
<CodeGroupItem title="vue2" active>
@[code vue](@/examples/firstHouse/vue2.vue)
</CodeGroupItem>
<CodeGroupItem title="vue3">
@[code vue](@/examples/firstHouse/vue3.vue)
</CodeGroupItem>
<CodeGroupItem title="react">
@[code tsx](@/examples/firstHouse/react.tsx)
</CodeGroupItem>
<CodeGroupItem title="原生">
@[code html](@/examples/firstHouse/html.html)
</CodeGroupItem>
<CodeGroupItem title="demo.css">
@[code css](@/examples/firstHouse/demo.css)
</CodeGroupItem>
</CodeGroup>
================================================
FILE: packages/doc/libs/vr360-core/events.md
================================================
# 事件
## 显示提示
#### 介绍
```ts
interface Vr360Events {
/**
* 触发提示时的回调
*/
showTip: (e: {
/**
* 提示配置信息
*/
tip: Tip
/**
* 相对于 container 的 left
*/
left: number
/**
* 相对于 container 的 top
*/
top: number
}) => void
}
```
点击查看 [tip](./methods.md#tip-空间配置里的提示配置) 类型
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
vr360.on('showTip', (e) => {
console.log('触发提示时的回调', e)
})
```
## 隐藏提示
#### 介绍
```ts
interface Vr360Events {
/**
* 隐藏提示时的回调
*/
hideTip: (e: {
/**
* 提示配置信息
*/
tip: Tip
}) => void
}
```
点击查看 [tip](./methods.md#tip-空间配置里的提示配置) 类型
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
vr360.on('hideTip', (e) => {
console.log('隐藏提示时的回调', e)
})
```
## 点击提示图标
#### 介绍
```ts
interface Vr360Events {
/**
* 点击提示时的回调
*/
clickTip: (e: {
/**
* 提示配置信息
*/
tip: Tip
}) => void
}
```
点击查看 [tip](./methods.md#tip-空间配置里的提示配置) 类型
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
vr360.on('clickTip', (e) => {
console.log('点击提示时的回调', e)
})
```
## 每一帧更新
#### 介绍
```ts
interface Vr360Events {
/**
* 每帧更新回调
*/
update: () => void
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
vr360.on('update', () => {
console.log('每帧更新回调')
})
```
## 切换全景空间完成
#### 介绍
```ts
interface Vr360Events {
/**
* 完成跳转空间时的回调
*/
afterSwitchSpace: (e: {
/**
* 跳转的目标空间配置
*/
spaceConfig: SpaceConfig
}) => void
}
```
点击查看 [SpaceConfig](./methods.md#spaceconfig-构造参数里的空间配置) 类型
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
vr360.on('afterSwitchSpace', (e) => {
console.log('完成跳转空间时的回调', e)
})
```
================================================
FILE: packages/doc/libs/vr360-core/example.md
================================================
# 示例
## 效果
<br/>
<DemoA></DemoA>
### vue2 实现
::: demo vue2 -- vue2 示例代码 -- 这里是示例代码的描述
```vue src/Example.vue
/*# @/examples/firstHouse/vue2.vue #*/
```
:::
### vue3 实现
::: demo vue3 -- vue3 示例代码 -- 这里是示例代码的描述
```vue src/Example.vue
/*# @/examples/firstHouse/vue3.vue #*/
```
:::
### react 实现
::: demo react -- react 示例代码 -- 这里是示例代码的描述
```tsx src/Example.tsx
/*# @/examples/firstHouse/react.tsx #*/
```
:::
### 原生实现
::: demo html -- html 示例代码 -- 这里是示例代码的描述
```html index.html
/*# @/examples/firstHouse/html.html #*/
```
:::
================================================
FILE: packages/doc/libs/vr360-core/methods.md
================================================
# 方法
## 构造器
#### 介绍
```ts
class Vr360 {
/**
* @param options 构造参数配置
* @returns Vr360 实例
*/
constructor(options: Vr360Options) {
...
}
}
```
点击查看 [Vr360Options](#vr360options-构造参数) 类型
#### 使用
```ts
import {Vr360, Vr360Options} from '@nicepkg/vr360-core'
const vr360Options: Vr360Options = {
container: document.querySelector('#container'),
tipContainer: document.querySelector('#tip-container'),
spacesConfig: []
}
const vr360 = new Vr360(vr360Options)
```
#### 相关类型
##### Vr360Options 构造参数
```ts
/**
* vr360 的构造参数
*/
interface Vr360Options {
/**
* 容器
*/
container: HTMLElement
/**
* 提示的 element 节点
*/
tipContainer?: HTMLElement
/**
* 初始显示的空间 id
*/
initSpaceId?: string
/**
* 空间配置
*/
spacesConfig: SpaceConfig[]
}
```
##### SpaceConfig 构造参数里的空间配置
```ts
/**
* 空间配置
*/
interface SpaceConfig {
/**
* 空间 id
*/
id: string
/**
* 相机配置
*/
camera?: {
/**
* 位置
*/
position: {
x: number
y: number
z: number
}
/**
* 缩放
*/
scale?: {
x: number
y: number
z: number
}
/**
* 旋转
*/
rotate?: {
x: number
y: number
z: number
}
}
/**
* 提示配置列表
*/
tips?: Tip[]
/**
* 空间贴图列表
*/
cubeSpaceTextureUrls: {
/**
* 左侧贴图 url
*/
left: string
/**
* 右侧贴图 url
*/
right: string
/**
* 上侧贴图 url
*/
up: string
/**
* 下侧贴图 url
*/
down: string
/**
* 前侧贴图 url
*/
front: string
/**
* 后侧贴图 url
*/
back: string
}
}
```
##### Tip 空间配置里的提示配置
```ts
interface Tip {
/**
* 提示 id
*/
id: string
/**
* 跳转的空间 id
*/
targetSpaceId?: string
/**
* 提示图案纹理贴图
*/
textureUrl?: string
/**
* 位置
*/
position: {
x: number
y: number
z: number
}
/**
* 缩放
*/
scale?: {
x: number
y: number
z: number
}
/**
* 旋转
*/
rotate?: {
x: number
y: number
z: number
}
/**
* 其它携带信息,比如你可以附加 title 、 content,在提示相关事件回调时你可以拿到
*/
[key: string]: any
}
```
## 监听页面尺寸变化
#### 介绍
```ts
class Vr360 {
/**
* 监听页面尺寸变化,更新画布尺寸
* @returns 返回取消监听函数
*/
public listenResize(): () => void {
...
}
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 让渲染器监听页面尺寸变化,实时更新画布尺寸
const removeResizeListener = vr360.listenResize()
// 你可以随时移除监听
removeResizeListener()
```
## 刷新容器渲染宽高
#### 介绍
```ts
class Vr360 {
/**
* 刷新容器渲染宽高
*/
public updateContainerSize(): void {
...
}
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 手动更新画布尺寸
vr360.updateContainerSize()
```
## 更新全景空间配置
#### 介绍
```ts
class Vr360 {
/**
* 更新 spacesConfig
* @param newSpacesConfig 新的空间配置
*/
public updateSpacesConfig(newSpacesConfig: SpaceConfig[]): void {
...
}
}
```
点击查看 [SpaceConfig](#spaceconfig-构造参数里的空间配置) 类型
#### 使用
```ts
import {Vr360, SpaceConfig} from '@nicepkg/vr360-core'
const spacesConfig: SpaceConfig[] = []
const vr360 = new Vr360({
container: document.querySelector('#container'),
tipContainer: document.querySelector('#tip-container'),
spacesConfig
})
vr360.render()
spacesConfig.push([
{
....
}
])
// 手动更新空间配置
vr360.updateSpacesConfig(spacesConfig)
```
## 切换全景空间
#### 介绍
```ts
class Vr360 {
/**
* 切换空间
* @param id 空间 id
*/
public switchSpace(id: string): void {
...
}
}
```
#### 使用
```ts
import {Vr360, SpaceConfig} from '@nicepkg/vr360-core'
const spacesConfig: SpaceConfig[] = [
{
id: 'spaceA',
...
},
{
id: 'spaceB',
...
}
]
const vr360 = new Vr360({
container: document.querySelector('#container'),
tipContainer: document.querySelector('#tip-container'),
spacesConfig
})
vr360.render()
// 切换到 spaceB 空间
vr360.switchSpace('spaceB')
```
## 开始渲染全景
#### 介绍
```ts
class Vr360 {
/**
* 渲染
*/
public render(): void {
...
}
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
```
## 把鼠标位置转换成 threejs 位置
#### 介绍
```ts
class Vr360 {
/**
* 根据鼠标位置获取当前空间的位置
* @param x 鼠标 x 坐标
* @param y 鼠标 y 坐标
* @returns 返回当前空间的位置
*/
public getPositionFromMouseXY(x: number, y: number): {
x: number
y: number
z: number
} {
...
}
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({
container: document.querySelector('#container'),
tipContainer: document.querySelector('#tip-container'),
spacesConfig: [....]
})
vr360.render()
// 获取当前空间的位置
document.querySelector('#container').addEventListener('click', (e) => {
const {x, y, z} = vr360.getPositionFromMouseXY(e.clientX, e.clientY)
console.log('点击的画布里的空间位置为:', x, y, z)
})
```
## 注销全景实例
#### 介绍
```ts
class Vr360 {
/**
* 销毁实例
*/
public destroy(): void {
...
}
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 销毁实例
vr360.destroy()
```
================================================
FILE: packages/doc/libs/vr360-core/properties.md
================================================
# 属性
## 全景容器
##### 介绍
```ts
class Vr360 {
/**
* 容器
*/
public container: HTMLElement
}
```
##### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 获取容器
console.log('3d 渲染容器是:', vr360.container)
```
## 相机
##### 介绍
```ts
class Vr360 {
/**
* 相机
*/
public camera: THREE.PerspectiveCamera
}
```
##### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 获取相机
console.log('3d 相机是:', vr360.camera)
```
## 渲染器
#### 介绍
```ts
class Vr360 {
/**
* 渲染器
*/
public renderer: THREE.WebGLRenderer
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 获取渲染器
console.log('3d 渲染器是:', vr360.renderer)
```
## 场景
#### 介绍
```ts
class Vr360 {
/**
* 场景
*/
public scene: THREE.Scene
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 获取场景
console.log('3d 场景是:', vr360.scene)
```
## 控制器
#### 介绍
```ts
class Vr360 {
/**
* 控制器
*/
public controls: THREE.OrbitControls
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 获取控制器
console.log('3d 控制器是:', vr360.controls)
```
## 全景空间配置
#### 介绍
```ts
class Vr360 {
/**
* 空间配置
*/
public spacesConfig: SpaceConfig[]
}
```
点击查看 [SpaceConfig](./methods.md#spaceconfig-构造参数里的空间配置) 类型
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 获取空间配置
console.log('3d 空间配置是:', vr360.spacesConfig)
```
## 提示容器
#### 介绍
```ts
class Vr360 {
/**
* 提示容器
*/
public tipContainer?: HTMLElement
}
```
#### 使用
```ts
import {Vr360} from '@nicepkg/vr360-core'
const vr360 = new Vr360({....})
vr360.render()
// 获取提示容器
console.log('3d 提示容器是:', vr360.tipContainer)
```
================================================
FILE: packages/doc/libs/vr360-ui/README.md
================================================
# 介绍
(开发中...)提供一个现成的 vr360 viewer 和 editor 组件,基于 stencil 构建的 web component。
================================================
FILE: packages/doc/libs/vr360-ui-react/README.md
================================================
# 介绍
(开发中...)提供一个现成的 vr360 viewer 和 editor 组件,适配 react 框架版。
================================================
FILE: packages/doc/libs/vr360-ui-vue2/README.md
================================================
# 介绍
(开发中...)提供一个现成的 vr360 viewer 和 editor 组件,适配 vue2 框架版。
================================================
FILE: packages/doc/libs/vr360-ui-vue3/README.md
================================================
# 介绍
(开发中...)提供一个现成的 vr360 viewer 和 editor 组件,适配 vue3 框架版。
================================================
FILE: packages/doc/package.json
================================================
{
"name": "doc",
"version": "0.3.1",
"private": true,
"description": "the libs docs website",
"scripts": {
"build": "vuepress build --clean-cache",
"clean": "rimraf .vuepress/.temp .vuepress/.cache .vuepress/dist",
"dev": "vuepress dev --clean-cache",
"serve": "http-server --port 8080 .vuepress/dist"
},
"dependencies": {
"@nicepkg/vr360-core": "workspace:*",
"@stackblitz/sdk": "^1.8.0",
"@vueuse/core": "^9.3.0",
"vue-router": "^4.1.5"
},
"devDependencies": {
"@types/markdown-it-container": "^2.0.5",
"@vuepress/bundler-vite": "2.0.0-beta.51",
"@vuepress/cli": "2.0.0-beta.51",
"@vuepress/client": "2.0.0-beta.51",
"@vuepress/core": "2.0.0-beta.51",
"@vuepress/markdown": "2.0.0-beta.51",
"@vuepress/plugin-google-analytics": "2.0.0-beta.51",
"@vuepress/plugin-register-components": "2.0.0-beta.51",
"@vuepress/plugin-shiki": "2.0.0-beta.51",
"@vuepress/shared": "2.0.0-beta.51",
"@vuepress/theme-default": "2.0.0-beta.51",
"@vuepress/utils": "2.0.0-beta.51",
"http-server": "^14.1.1",
"js-base64": "^3.7.2",
"markdown-it": "^13.0.1",
"markdown-it-container": "^3.0.0",
"rimraf": "^3.0.2",
"sass": "^1.55.0",
"vite": "*",
"vue": "^3.2.40",
"vuepress": "2.0.0-beta.51"
}
}
================================================
FILE: packages/doc/tsconfig.json
================================================
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist",
"types": ["vite/client", "@vuepress/client/types"],
"paths": {
"@/*": ["./.vuepress/*"]
}
},
"include": ["./.vuepress/**/*", "./*.js", "./*.ts"],
"exclude": ["node_modules", "dist", ".vuepress/examples/**/*"]
}
================================================
FILE: packages/doc/vuepress.config.ts
================================================
import {defineUserConfig} from 'vuepress'
import {path} from '@vuepress/utils'
import {viteBundler} from '@vuepress/bundler-vite'
import {plugins} from './.vuepress/plugins'
import {bundlerConfig} from './bundler.config'
import {localTheme} from './.vuepress/theme'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const pathResolve = (..._path: string[]) => path.resolve(__dirname, ..._path)
export default defineUserConfig({
base: '/',
head: [
[
'link',
{
rel: 'icon',
type: 'image/png',
sizes: '16x16',
href: `/images/icons/favicon-16x16.png`
}
],
[
'link',
{
rel: 'icon',
type: 'image/png',
sizes: '32x32',
href: `/images/icons/favicon-32x32.png`
}
],
['link', {rel: 'manifest', href: '/manifest.webmanifest'}],
['meta', {name: 'application-name', content: 'Vr360'}],
['meta', {name: 'apple-mobile-web-app-title', content: 'Vr360'}],
['meta', {name: 'apple-mobile-web-app-status-bar-style', content: 'black'}],
['link', {rel: 'apple-touch-icon', href: `/images/icons/apple-touch-icon.png`}],
[
'link',
{
rel: 'mask-icon',
href: '/images/icons/safari-pinned-tab.svg',
color: '#0c4dc4'
}
],
['meta', {name: 'msapplication-TileColor', content: '#0c4dc4'}],
['meta', {name: 'theme-color', content: '#0c4dc4'}]
],
// site-level locales config
locales: {
'/': {
lang: 'zh-CN',
title: 'Vr360',
description: '快速实现你的全景浏览开发需求'
}
// '/en/': {
// lang: 'en-US',
// title: 'Vr360',
// description: 'Quickly realize your panoramic browsing development needs'
// }
},
theme: localTheme(),
bundler: viteBundler(bundlerConfig),
templateDev: pathResolve('.vuepress/index.build.html'),
templateBuild: pathResolve('.vuepress/index.build.html'),
markdown: {
importCode: {
handleImportPath: str => str.replace(/^@/, pathResolve('.vuepress'))
}
},
plugins
})
================================================
FILE: packages/vr360-core/CHANGELOG.md
================================================
# 0.3.0 (2022-10-08)
### Bug Fixes
- **@nicepkg/vr360-core:** 修复移动端的提示移动 bug ([7090a98](https://github.com/nicepkg/vr360/commit/7090a9805bc472240de6ad4467bbadc14fd6151d))
# 0.3.0 (2022-10-02)
## 0.2.1 (2022-10-02)
# 0.2.0 (2022-10-02)
### Bug Fixes
- 修复 vr360-core tips 在滑动时的 bug ([e179ebc](https://github.com/nicepkg/vr360/commit/e179ebc2697314bc455320eecf8beb6182a53ded))
### Features
- 暴露更多的事件,设置默认自动旋转 ([4e21c53](https://github.com/nicepkg/vr360/commit/4e21c53ac945532020a7fbbfa46644294c33b49d))
- 初始化项目 ([06f39d1](https://github.com/nicepkg/vr360/commit/06f39d141004a1d0b1a125ad598298baf15ffee8))
- 添加标签提示和空间切换功能 ([6aa67d3](https://github.com/nicepkg/vr360/commit/6aa67d39113b06d05036ecc1d66ab1d70a2f4cf5))
- 添加如视 vr 导入支持 ([5a9eb5d](https://github.com/nicepkg/vr360/commit/5a9eb5d7a33d092be8cea5565490f268376d2f79))
- 文档添加示例插件、core 的纹理 top、bottom 改为 up 和 down ([70ccb2c](https://github.com/nicepkg/vr360/commit/70ccb2c40a06079ffb3cf50b27f986ab19b1f7db))
- 修改 package.json 说明 ([e5e4ad0](https://github.com/nicepkg/vr360/commit/e5e4ad04b1c7a9cddff3af6f73d438fd1de85b25))
- 优化代码 ([4519c4a](https://github.com/nicepkg/vr360/commit/4519c4a0fb230bb62bed49f97e0824c8977be3ca))
- 优化 sdk,添加纹理缓存预加载 ([9dd57e7](https://github.com/nicepkg/vr360/commit/9dd57e71b8f5a38ecc9901395a9b189481172edb))
- 重写核心,添加最小化更新配置支持 ([289091b](https://github.com/nicepkg/vr360/commit/289091b13dfe0495b44ae9e5353b78d2712e9762))
================================================
FILE: packages/vr360-core/README.md
================================================
<div align="center">
<a href="https://vr360.nicepkg.cn/libs/vr360-core/">
<img src="https://vr360.nicepkg.cn/images/logo-bg.png" width="50%">
</a>
<h1>@nicepkg/vr360-core</h1>
<p>json 驱动的全景浏览库核心,设计框架无关性,可用于任何框架,如vue/react/angular/svelte/solidjs...</p>
<p>
<img src="https://img.shields.io/npm/v/@nicepkg/vr360-core?style=flat-square" alt="version">
<img src="https://img.shields.io/npm/dependency-version/@nicepkg/vr360-core/peer/three" alt="three">
<img src="https://img.shields.io/npm/l/@nicepkg/vr360-core.svg" alt="license">
<img src="https://img.shields.io/codecov/c/github/nicepkg/vr360" alt="coverage">
<img src="https://img.badgesize.io/https://unpkg.com/@nicepkg/vr360-core?compression=gzip&label=gzip" alt="gzip" />
<img src="https://img.shields.io/github/stars/nicepkg/vr360?style=social" alt="stars">
</p>
</div>
## 介绍
`@nicepkg/vr360-core` 是一个基于 [threejs](https://github.com/mrdoob/three.js/) 的全景库,非常适合用来做全景看房、全景街景、全景景点等业务需求。
它支持 json 配置驱动视图,你可以用 json 配置的方式快速实现常见全景需求。
## 特性
- json 驱动视图,快速实现全景业务需求
- 高性能,支持自动找出 json 变更的部分,最小化更新到 3d 全景里,不会整个场景重建。
- 支持增删改提示点,hover 提示点的弹窗支持自定义定制,支持自定义提示点的贴图。
- 内置场景切换穿梭动画
- 暴露 scene、camera、renderer 等 [threejs](https://github.com/mrdoob/three.js/) 对象,方便你更高的定制化
- 基于事件驱动的数据交互,设计框架无关性
- 完整的 ts 支持
## 安装
for npm
```bash
npm i @nicepkg/vr360-core threejs
```
for yarn
```bash
yarn add @nicepkg/vr360-core threejs
```
for pnpm
```bash
pnpm add @nicepkg/vr360-core threejs
```
## 使用
点击查看[使用文档](https://vr360.nicepkg.cn/libs/vr360-core/)。
## Contributing
Learn about contribution [here](https://github.com/nicepkg/vr360/blob/master/CONTRIBUTING.md).
This project exists thanks to all the people who contribute:
<a href="https://github.com/nicepkg/vr360/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nicepkg/vr360" />
</a>
## License
[MIT](https://github.com/nicepkg/vr360/blob/master/LICENSE) License © 2022-PRESENT [nicepkg](https://github.com/nicepkg)
================================================
FILE: packages/vr360-core/package.json
================================================
{
"name": "@nicepkg/vr360-core",
"version": "0.3.1",
"description": "快速实现你的全景开发需求,全景看房、全景街景、全景景点",
"keywords": [
"vr360",
"vr360-core",
"panorama",
"pannellum",
"photo-sphere-viewer",
"photograph",
"nicepkg",
"webgl",
"threejs"
],
"author": "yangjinming <https://github.com/2214962083>",
"funding": "https://github.com/sponsors/2214962083",
"license": "MIT",
"private": false,
"scripts": {
"build": "pnpm type-check &&esno ./scripts/build.ts",
"build:watch": "cross-env WATCH=true pnpm build",
"clean": "rimraf ./dist/**/*",
"dev": "esno ./src/playground.ts",
"test": "vitest run --silent --passWithNoTests",
"test:watch": "pnpm test -- --watch",
"type-check": "tsc --noEmit"
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"unpkg": "./dist/index.min.umd.js",
"jsdelivr": "./dist/index.min.umd.js",
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
},
"./*": "./*"
},
"files": [
"dist"
],
"sideEffects": false,
"repository": {
"type": "git",
"url": "git+https://github.com/nicepkg/vr360.git",
"directory": "packages/vr360-core"
},
"bugs": {
"url": "https://github.com/nicepkg/vr360/issues"
},
"homepage": "https://github.com/nicepkg/vr360#readme",
"peerDependencies": {
"three": "*"
},
"dependencies": {
"@tweenjs/tween.js": "^18.6.4",
"eventemitter3": "^4.0.7"
},
"devDependencies": {
"@nicepkg/vr360-shared": "workspace:*",
"@types/three": "^0.144.0",
"@types/node": "*",
"esno": "*",
"jsdom": "^20.0.1",
"three": "^0.145.0",
"typescript": "*",
"vite": "*",
"vitest": "*"
}
}
================================================
FILE: packages/vr360-core/scripts/build.ts
================================================
import {buildUtils} from '@nicepkg/vr360-shared'
import {minifyConfig, unMinifyConfig, packagePath} from '../vite.config'
buildUtils.build({
minifyConfig,
unMinifyConfig,
packagePath,
dtsOptions: {
// merge all .d.ts files
rollupTypes: true
}
})
================================================
FILE: packages/vr360-core/src/helper.ts
================================================
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
import * as THREE from 'three'
import type {DeepRequired, DeepPartial, Position, ThreeObjectBase} from './types'
let raycaster: THREE.Raycaster
let mouse: THREE.Vector2
function getSingleValue() {
if (!raycaster) raycaster = new THREE.Raycaster()
if (!mouse) mouse = new THREE.Vector2()
return {
raycaster,
mouse
}
}
export type ConvertMousePositionToThreePositionOptions = {
/**
* 鼠标 x 坐标
*/
mouseX: number
/**
* 鼠标 y 坐标
*/
mouseY: number
/**
* 获取依赖函数
*/
getDeps: GetAddListenerToThreeObjectDeps
}
/**
* 转换鼠标位置到 three 坐标
* @param options 配置
* @returns x,y,z 坐标
*/
export function convertMousePositionToThreePosition(options: ConvertMousePositionToThreePositionOptions): Position {
const {mouseX, mouseY, getDeps} = options
const {camera, scene, renderer} = getDeps()
if (!camera || !scene || !renderer) return {x: 0, y: 0, z: 0}
const {raycaster, mouse} = getSingleValue()
const renderDomBound = renderer.domElement.getBoundingClientRect()
mouse.x = ((mouseX - renderDomBound.left) / renderer.domElement.clientWidth) * 2 - 1
mouse.y = -((mouseY - renderDomBound.top) / renderer.domElement.clientHeight) * 2 + 1
raycaster.setFromCamera(mouse, camera)
const intersects = raycaster.intersectObjects(scene.children)
const firstIntersect = intersects[0] ?? undefined
return firstIntersect.point ?? {x: 0, y: 0, z: 0}
}
/**
* 获取依赖函数
*/
export type GetAddListenerToThreeObjectDeps = () => {
/**
* 摄像机实例
*/
camera?: THREE.PerspectiveCamera
/**
* 场景实例
*/
scene?: THREE.Scene
/**
* 渲染器实例
*/
renderer?: THREE.WebGLRenderer
}
export type ThreeObjectDispatchEvent<T extends keyof HTMLElementEventMap> = {
type: T
intersect: THREE.Intersection
sourceEvent: HTMLElementEventMap[T]
isMouseDown: boolean
}
/**
* 为 threejs 对象添加事件监听器
* @param getDeps 获取依赖函数
* @param events 监听的事件名称列表
*/
export function addListenerToThreeObject(
getDeps: GetAddListenerToThreeObjectDeps,
events: (keyof HTMLElementEventMap)[] = ['click', 'mousemove', 'touchmove', 'mousedown', 'mouseup']
) {
const renderElement = getDeps().renderer?.domElement
if (!renderElement) return
const {raycaster, mouse} = getSingleValue()
// 上一个点击的对象
let mouseoverPreIntersect: THREE.Intersection | undefined
// 鼠标是否处于按压状态,用于传给 tip 判断,在按下鼠标滑动时不显示 tip
let isMouseDown = false
;['mousedown'].map(eventName =>
renderElement.addEventListener(eventName, () => {
isMouseDown = true
})
)
;['mouseup'].map(eventName =>
renderElement.addEventListener(eventName, () => {
isMouseDown = false
})
)
function handleEvent(eventName: string, event: MouseEvent | TouchEvent) {
event.preventDefault()
const {camera, scene, renderer} = getDeps()
if (!camera || !scene || !renderer) return
const renderDomBound = renderer.domElement.getBoundingClientRect()
const clientX = (event as TouchEvent)?.changedTouches?.[0]?.clientX ?? (event as MouseEvent).clientX
const clientY = (event as TouchEvent)?.changedTouches?.[0]?.clientY ?? (event as MouseEvent).clientY
mouse.x = ((clientX - renderDomBound.left) / renderer.domElement.clientWidth) * 2 - 1
mouse.y = -((clientY - renderDomBound.top) / renderer.domElement.clientHeight) * 2 + 1
raycaster.setFromCamera(mouse, camera)
const intersects = raycaster.intersectObjects(scene.children)
const firstIntersect = intersects[0] ?? undefined
if (!firstIntersect) return
if (
['mousemove', 'touchmove'].includes(eventName) &&
mouseoverPreIntersect?.object.uuid !== firstIntersect.object.uuid
) {
// 在 mousemove 事件时, 并且本次鼠标指中的 three 对象和上一个对象不相同时
// 触发上个 hover 对象的 mouseout 事件
mouseoverPreIntersect?.object.dispatchEvent({
type: 'mouseout',
intersect: mouseoverPreIntersect,
sourceEvent: event,
isMouseDown
} as ThreeObjectDispatchEvent<'mouseout'>)
mouseoverPreIntersect = firstIntersect
// 设置鼠标样式
const cursor = firstIntersect.object.userData.cursor || 'default'
if (renderElement && renderElement.style.cursor !== cursor) renderElement.style.cursor = cursor
// 触发本次 hover 对象的 mouseover 事件
firstIntersect.object.dispatchEvent({
type: 'mouseover',
intersect: firstIntersect,
sourceEvent: event,
isMouseDown
} as ThreeObjectDispatchEvent<'mouseover'>)
}
// 射线范围内没有东西,终止流程
if (intersects.length <= 0) return
// if (eventName === 'click') {
// console.log(`当前点击位置: x=${firstIntersect.point.x}, y=${firstIntersect.point.y}, z=${firstIntersect.point.z}`)
// }
firstIntersect.object.dispatchEvent({
type: eventName === 'touchmove' ? 'mousemove' : eventName,
intersect: firstIntersect,
sourceEvent: event,
isMouseDown
} as ThreeObjectDispatchEvent<keyof HTMLElementEventMap>)
}
for (const eventName of events) {
renderElement.addEventListener(eventName, event => handleEvent(eventName, event as MouseEvent))
}
}
/**
* 纹理缓存加载器
*/
export class TextureCacheLoader {
static instance: TextureCacheLoader
static getInstance() {
if (!TextureCacheLoader.instance) TextureCacheLoader.instance = new TextureCacheLoader()
return TextureCacheLoader.instance
}
/**
* 缓存
*/
private cache = new Map<string, THREE.Texture>()
/**
* 加载器
*/
private loader = new THREE.TextureLoader()
/**
* 根据单个链接加载纹理
* @param url 图片链接
* @returns 返回纹理
*/
loadUrl(url: string) {
if (this.cache.has(url)) return this.cache.get(url)
const texture = this.loader.load(url)
this.cache.set(url, texture)
return texture
}
/**
* 根据多个链接加载纹理
* @param urls 图片链接数组
* @returns 返回纹理数组
*/
loadUrls(urls: string[]) {
return urls.map(url => this.loadUrl(url))
}
}
/**
* 判断该对象是否含有该键名
* @param obj 对象
* @param key 键名
* @returns 返回该对象是否含有该键名
*/
export function hasOwnProperty(obj: any, key: string): boolean {
return Object.prototype.hasOwnProperty.call(obj, key)
}
/**
* 判断两个值是否相等
* @param a 值 a
* @param b 值 b
* @returns 值 a 和 值 b 是否相等
*/
export function isEqual(a: any, b: any): boolean {
if (a === b) return true
if (typeof a !== typeof b) return false
if (typeof a !== 'object') return false
if (a === null || b === null) return false
if (Array.isArray(a) !== Array.isArray(b)) return false
if (Array.isArray(a)) {
if (a.length !== b.length) return false
for (const [i, element] of a.entries()) {
if (!isEqual(element, b[i])) return false
}
return true
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const aKeys = Object.keys(a)
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const bKeys = Object.keys(b)
if (aKeys.length !== bKeys.length) return false
for (const key of aKeys) {
if (!isEqual(a[key], b[key])) return false
}
return true
}
/**
* 防抖
* @param func 函数
* @param wait 延迟时间,单位毫秒
* @param immediate 是否立即执行
* @returns 返回一个新的函数
*/
export function debounce<T extends (...args: any[]) => any>(func: T, wait: number, immediate = true) {
let timeout: number | undefined
let hasRunImmediate = false
return function (this: any, ...args: Parameters<T>) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
const run = () => func.apply(this, args)
if (timeout) clearTimeout(timeout)
if (immediate && !hasRunImmediate) {
hasRunImmediate = true
run()
} else {
timeout = window.setTimeout(run, wait)
}
} as T
}
/**
* 更新 threejs 3d 对象的位置、缩放、旋转等基础信息
* @param object threejs 3d 对象
* @param baseInfo 位置、缩放、旋转信息
*/
export function update3dObjectBaseInfo(object: THREE.Object3D, baseInfo: Partial<ThreeObjectBase>): void {
const {position, rotate, scale} = baseInfo
if (position && !new THREE.Vector3(position.x, position.y, position.z).equals(object.position)) {
object.position.set(position.x, position.y, position.z)
}
if (scale && !new THREE.Vector3(scale.x, scale.y, scale.z).equals(object.scale)) {
object.scale.set(scale.x, scale.y, scale.z)
}
if (rotate && !new THREE.Euler(rotate.x, rotate.y, rotate.z).equals(object.rotation)) {
object.rotation.set(rotate.x, rotate.y, rotate.z)
}
}
/**
* 获取 threejs 3d 对象的位置、缩放、旋转等基础信息
* @param object threejs 3d 对象
* @returns 返回 threejs 3d 对象的位置、缩放、旋转等基础信息
*/
export function get3dObjectBaseInfo(object?: THREE.Object3D): ThreeObjectBase {
return {
position: {
x: object?.position?.x ?? 0,
y: object?.position?.y ?? 0,
z: object?.position?.z ?? 0
},
rotate: {
x: object?.rotation?.x ?? 0,
y: object?.rotation?.y ?? 0,
z: object?.rotation?.z ?? 0
},
scale: {
x: object?.scale?.x ?? 1,
y: object?.scale?.y ?? 1,
z: object?.scale?.z ?? 1
}
}
}
/**
* 处理位置、缩放、旋转等基础信息默认值
* @param baseInfo 位置、缩放、旋转信息
* @returns 返回位置、缩放、旋转等基础信息
*/
export function formatBaseInfo(baseInfo: DeepPartial<ThreeObjectBase> | undefined): DeepRequired<ThreeObjectBase> {
return {
position: {
x: baseInfo?.position?.x ?? 0,
y: baseInfo?.position?.y ?? 0,
z: baseInfo?.position?.z ?? 0
},
rotate: {
x: baseInfo?.rotate?.x ?? 0,
y: baseInfo?.rotate?.y ?? 0,
z: baseInfo?.rotate?.z ?? 0
},
scale: {
x: baseInfo?.scale?.x ?? 1,
y: baseInfo?.scale?.y ?? 1,
z: baseInfo?.scale?.z ?? 1
}
}
}
================================================
FILE: packages/vr360-core/src/index.ts
================================================
export * from './vr360'
export * from './types'
export * from './manager'
================================================
FILE: packages/vr360-core/src/manager/index.ts
================================================
export * from './space'
export * from './tip'
export type ConfigModel = {
id: string
}
export type ConfigModelManager<CM extends ConfigModel, TM> = {
create(configModel: CM): TM
add(configModel: CM): TM
update(configModel: Partial<CM> & Pick<CM, 'id'>): TM | undefined
find(configModel: string | (Partial<CM> & Pick<CM, 'id'>)): TM | undefined
findOrCreate(configModel: CM): TM
remove(configModel: string | (Partial<CM> & Pick<CM, 'id'>)): void
removeAll(): void
destroy(): void
}
================================================
FILE: packages/vr360-core/src/manager/space.ts
================================================
/* eslint-disable @typescript-eslint/ban-ts-comment */
import * as THREE from 'three'
import type {TextureCacheLoader} from '../helper'
import {isEqual} from '../helper'
import {EventEmitter} from 'eventemitter3'
import type {ConfigModelManager} from './index'
import type {CubeSpaceTextureUrls, SpaceConfig} from '../types'
import type {TipManagerEvents, TipManagerOptions} from './tip'
import {tipEventNames, TipManager} from './tip'
/**
* 空间管理器事件列表
*/
export type SpaceManagerEvents = TipManagerEvents
export type SpaceEventName = keyof SpaceManagerEvents
export const spaceEventNames: SpaceEventName[] = [...tipEventNames]
export type SpaceManagerOptions = Omit<TipManagerOptions, 'tipContainer'> & {
tipContainer?: HTMLElement
}
/**
* curd SpaceConfig to threejs
*/
export class SpaceManager
extends EventEmitter<SpaceManagerEvents>
implements ConfigModelManager<SpaceConfig, THREE.Group>
{
private container: HTMLElement
private camera: THREE.PerspectiveCamera
private scene: THREE.Scene
private renderer: THREE.WebGLRenderer
private parent: THREE.Object3D
private tipContainer?: HTMLElement
private textureCacheLoader: TextureCacheLoader
private spaceIdGroupMap = new Map<string, THREE.Group>()
private spaceIdTipManager = new Map<string, TipManager>()
constructor(options: SpaceManagerOptions) {
super()
this.container = options.container
this.camera = options.camera
this.scene = options.scene
this.renderer = options.renderer
this.parent = options.parent
this.tipContainer = options.tipContainer
this.textureCacheLoader = options.textureCacheLoader
}
/**
* 创建提示管理器
* @param parent threejs 组
* @returns 提示管理器
*/
private createTipManager(parent: THREE.Object3D): TipManager | undefined {
if (!this.tipContainer) return
const tipManager = new TipManager({
container: this.container,
camera: this.camera,
scene: this.scene,
renderer: this.renderer,
tipContainer: this.tipContainer,
textureCacheLoader: this.textureCacheLoader,
parent
})
// 提示的事件继承
tipEventNames.forEach(eventName => {
tipManager.on(eventName, e => {
// @ts-ignore
this.emit(eventName, e)
})
})
return tipManager
}
/**
* 创建正方体空间 mesh
* @param cubeSpaceTextureUrls 空间贴图
*/
private createCubeSpaceMesh(cubeSpaceTextureUrls: CubeSpaceTextureUrls) {
// 创建空间
const boxGeometry = new THREE.BoxGeometry(100, 100, 100)
// 随机挑选一个面翻转扩大,使得贴图能够正常渲染
boxGeometry.scale(-1, 1, 1)
// 贴材质
const boxMaterials = this.createCubeSpaceMaterials(cubeSpaceTextureUrls)
const spaceMesh = new THREE.Mesh(boxGeometry, boxMaterials)
return spaceMesh
}
/**
* 创建正方体空间材料
* @param cubeSpaceTextureUrls 空间贴图
*/
private createCubeSpaceMaterials(cubeSpaceTextureUrls: CubeSpaceTextureUrls) {
const directions = ['right', 'left', 'up', 'down', 'front', 'back'] as const
const boxMaterials: THREE.MeshBasicMaterial[] = directions.map(direction => {
const texture = this.textureCacheLoader.loadUrl(
cubeSpaceTextureUrls[direction as keyof typeof cubeSpaceTextureUrls]
)
return new THREE.MeshBasicMaterial({map: texture})
})
return boxMaterials
}
public create(spaceConfig: SpaceConfig): THREE.Group {
const {cubeSpaceTextureUrls, tips = [], id} = spaceConfig
const group = new THREE.Group()
// 创建提示精灵
if (this.tipContainer) {
const tipManager = this.createTipManager(group)!
tips.forEach(tip => {
const sprite = tipManager.findOrCreate(tip)
group.add(sprite)
})
this.spaceIdTipManager.set(id, tipManager)
}
// 创建空间
const spaceMesh = this.createCubeSpaceMesh(cubeSpaceTextureUrls)
spaceMesh.userData.type = 'spaceMesh'
// 挂载当前 spaceConfig 到 group 上,在事件里面使用(更新时会覆盖这个挂载)
group.userData.type = 'spaceGroup'
group.userData.spaceConfig = spaceConfig
group.add(spaceMesh)
this.spaceIdGroupMap.set(id, group)
return group
}
public add(spaceConfig: SpaceConfig): THREE.Group {
const group = this.findOrCreate(spaceConfig)
this.parent.add(group)
return group
}
public update(spaceConfig: Partial<SpaceConfig> & Pick<SpaceConfig, 'id'>): THREE.Group | undefined {
const {id, tips, cubeSpaceTextureUrls} = spaceConfig
const group = this.spaceIdGroupMap.get(id)
if (!group) return
if (tips && this.tipContainer) {
const tipManager = this.spaceIdTipManager.get(id) ?? this.createTipManager(group)!
const sprites = new Set(
tips.map(tip => {
const sprite = tipManager.update(tip) ?? tipManager.create(tip)
return sprite
})
)
const children = [...group.children]
// 找出需要删除的精灵并删除
children.forEach(child => {
if (child.type === 'Sprite' && child.userData.type === 'tipSprite' && !sprites.has(child as THREE.Sprite)) {
group.remove(child)
}
})
// 找出需要添加的精灵并添加
sprites.forEach(sprite => {
if (!group.children.includes(sprite)) {
group.add(sprite)
}
})
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (cubeSpaceTextureUrls && !isEqual(group.userData.spaceConfig?.cubeSpaceTextureUrls, cubeSpaceTextureUrls)) {
let spaceMesh = group.children.find(child => child.type === 'Mesh' && child.userData.type === 'spaceMesh') as
| THREE.Mesh
| undefined
if (!spaceMesh) {
spaceMesh = this.createCubeSpaceMesh(cubeSpaceTextureUrls)
} else {
const boxMaterials = this.createCubeSpaceMaterials(cubeSpaceTextureUrls)
spaceMesh.material = boxMaterials
}
}
group.userData.spaceConfig = spaceConfig
// this.spaceIdGroupMap.set(id, group)
return group
}
public find(spaceConfig: string | (Partial<SpaceConfig> & Pick<SpaceConfig, 'id'>)): THREE.Group | undefined {
const id = typeof spaceConfig === 'string' ? spaceConfig : spaceConfig.id
return this.spaceIdGroupMap.get(id)
}
public findOrCreate(spaceConfig: SpaceConfig): THREE.Group {
const group = this.find(spaceConfig)
if (group) return group
return this.create(spaceConfig)
}
public remove(spaceConfig: string | (Partial<SpaceConfig> & Pick<SpaceConfig, 'id'>)): void {
const id = typeof spaceConfig === 'string' ? spaceConfig : spaceConfig.id
const group = this.spaceIdGroupMap.get(id)
if (!group) return
this.parent.remove(group)
this.spaceIdGroupMap.delete(id)
this.spaceIdTipManager.delete(id)
}
public removeAll(): void {
this.spaceIdGroupMap.forEach(group => {
this.parent.remove(group)
})
this.spaceIdTipManager.forEach(tipManager => {
tipManager.destroy()
})
this.spaceIdGroupMap.clear()
this.spaceIdTipManager.clear()
}
public destroy(): void {
this.removeAll()
this.removeAllListeners()
}
}
================================================
FILE: packages/vr360-core/src/manager/tip.ts
================================================
import * as THREE from 'three'
import type {TextureCacheLoader, ThreeObjectDispatchEvent} from '../helper'
import {update3dObjectBaseInfo} from '../helper'
import type {Tip} from '../types'
import defaultTipUrl from '../assets/tips.png'
import {EventEmitter} from 'eventemitter3'
import type {ConfigModelManager} from '../manager'
/**
* 触发提示时的回调 event
*/
export type ShowTipEvent = {
/**
* 提示配置信息
*/
tip: Tip
/**
* 相对于 container 的 left
*/
left: number
/**
* 相对于 container 的 top
*/
top: number
}
/**
* 隐藏提示时的回调 event
*/
export type HideTipEvent = {
tip: Tip
}
/**
* 点击提示时的回调 event
*/
export type ClickTipEvent = {
tip: Tip
}
/**
* 切换空间事件
*/
export type SwitchSpaceEvent = {
/**
* 跳转目标空间的 id
*/
targetSpaceId: string
/**
* 点击切换的位置
*/
clickPosition?: THREE.Vector3
}
/**
* 提示管理器事件列表
*/
export type TipManagerEvents = {
/**
* 触发提示时的回调
*/
showTip: (e: ShowTipEvent) => void
/**
* 隐藏提示时的回调
*/
hideTip: (e: HideTipEvent) => void
/**
* 点击提示时的回调
*/
clickTip: (e: ClickTipEvent) => void
/**
* 切换空间时的回调
*/
switchSpace: (e: SwitchSpaceEvent) => void
}
export type TipEventName = keyof TipManagerEvents
export const tipEventNames: TipEventName[] = ['showTip', 'hideTip', 'clickTip', 'switchSpace']
export type TipManagerOptions = {
/**
* 容器
*/
container: HTMLElement
/**
* 相机
*/
camera: THREE.PerspectiveCamera
/**
* 场景
*/
scene: THREE.Scene
/**
* 渲染器
*/
renderer: THREE.WebGLRenderer
/**
* 3d 组, 用于添加提示 sprite
*/
parent: THREE.Object3D
/**
* texture 缓存器
*/
textureCacheLoader: TextureCacheLoader
/**
* 提示容器
*/
tipContainer: HTMLElement
}
/**
* 提示配置管理器
*/
export class TipManager extends EventEmitter<TipManagerEvents> implements ConfigModelManager<Tip, THREE.Sprite> {
private container: HTMLElement
private camera: THREE.PerspectiveCamera
private scene: THREE.Scene
private renderer: THREE.WebGLRenderer
private parent: THREE.Object3D
private textureCacheLoader: TextureCacheLoader
private tipContainer: HTMLElement
private tipIdSpriteMap = new Map<string, THREE.Sprite>()
constructor(options: TipManagerOptions) {
super()
this.container = options.container
this.camera = options.camera
this.scene = options.scene
this.renderer = options.renderer
this.parent = options.parent
this.textureCacheLoader = options.textureCacheLoader
this.tipContainer = options.tipContainer
}
public create(tip: Tip): THREE.Sprite {
const {position, textureUrl = defaultTipUrl, scale, rotate, id} = tip
const texture = this.textureCacheLoader.loadUrl(textureUrl)
const material = new THREE.SpriteMaterial({map: texture})
const sprite = new THREE.Sprite(material)
// 调整位置大小旋转角度
sprite.scale.set(scale?.x ?? 3, scale?.y ?? 3, scale?.z ?? 3)
sprite.position.set(position.x, position.y, position.z)
if (rotate) {
sprite.rotation.set(rotate.x, rotate.y, rotate.z)
}
// 设置鼠标样式
sprite.userData.cursor = 'pointer'
// 挂载当前 tip 到 sprite 上,在事件里面使用(更新时会覆盖这个挂载)
sprite.userData.type = 'tipSprite'
sprite.userData.tip = tip
// 触发展示 tips 事件
const emitShowTip = (e: ThreeObjectDispatchEvent<'mouseover'> | ThreeObjectDispatchEvent<'mouseup'>) => {
// hover 的对象
const intersect = e.intersect
const tipFromUserData = intersect.object.userData.tip as Tip
const containerHalfWidth = this.container.clientWidth / 2
const containerHalfHeight = this.container.clientHeight / 2
// 注意:如果外面是使用 display none 控制 tip 显隐,这里的 tipContainer 宽高均为 0,导致 tip 偏移
const tipContainerWidth = this.tipContainer.clientWidth
const tipContainerHeight = this.tipContainer.clientHeight
const rendererOffsetLeft = this.renderer.domElement.offsetLeft
const rendererOffsetTop = this.renderer.domElement.offsetTop
const percentPosition = intersect.object.position.clone().project(this.camera)
const left = (percentPosition.x + 1) * containerHalfWidth - tipContainerWidth / 2 + rendererOffsetLeft
const top = (1 - percentPosition.y) * containerHalfHeight - tipContainerHeight / 2 + rendererOffsetTop
const showTipEvent: ShowTipEvent = {tip: tipFromUserData, left, top}
// console.log('展示提示', showTipEvent)
this.emit('showTip', showTipEvent)
}
// 鼠标 hover 时展示提示
sprite.addEventListener('mouseover', _e => {
const e = _e as unknown as ThreeObjectDispatchEvent<'mouseover'>
// 如果鼠标处于按压下状态,不展示提示,此举是为了防止和 controls 滑动冲突导致位置偏差
if (e.isMouseDown) return
emitShowTip(e)
})
// 如果在 tip 上松开鼠标,则判断为需要展示 tips,因为用户可能 controls 滑倒 tips 再松开
sprite.addEventListener('mouseup', _e => {
const e = _e as unknown as ThreeObjectDispatchEvent<'mouseup'>
emitShowTip(e)
})
// 鼠标移出时移除提示
sprite.addEventListener('mouseout', _e => {
const e = _e as unknown as ThreeObjectDispatchEvent<'mouseout'>
const intersect = e.intersect
const tipFromUserData = intersect.object.userData.tip as Tip
const hideTipEvent: HideTipEvent = {tip: tipFromUserData}
// console.log('隐藏提示', hideTipEvent)
this.emit('hideTip', hideTipEvent)
})
sprite.addEventListener('click', _e => {
const e = _e as unknown as ThreeObjectDispatchEvent<'click'>
const intersect = e.intersect
const tipFromUserData = intersect.object.userData.tip as Tip
this.emit('clickTip', {tip: tipFromUserData})
if (tipFromUserData.targetSpaceId) {
// 存在目标空间 id,点击跳转
// 跳转时隐藏提示
this.emit('hideTip', {tip: tipFromUserData})
const switchSpaceEvent: SwitchSpaceEvent = {
targetSpaceId: tipFromUserData.targetSpaceId,
clickPosition: intersect.point
}
// console.log('调转到空间', switchSpaceEvent)
this.emit('switchSpace', switchSpaceEvent)
}
})
this.tipIdSpriteMap.set(id, sprite)
return sprite
}
public add(tip: Tip): THREE.Sprite {
const sprite = this.findOrCreate(tip)
this.parent.add(sprite)
return sprite
}
public update(tip: Partial<Tip> & Pick<Tip, 'id'>): THREE.Sprite | undefined {
const {id, textureUrl, position, scale, rotate} = tip
const sprite = this.tipIdSpriteMap.get(id)
if (!sprite) return
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (textureUrl && textureUrl !== sprite.material.map?.image?.src) {
const texture = this.textureCacheLoader.loadUrl(textureUrl)
const material = new THREE.SpriteMaterial({map: texture})
sprite.material = material
}
update3dObjectBaseInfo(sprite, {
position,
scale,
rotate
})
sprite.userData.tip = tip
// this.tipIdSpriteMap.set(id, sprite)
return sprite
}
public find(tip: string | (Partial<Tip> & Pick<Tip, 'id'>)): THREE.Sprite | undefined {
const id = typeof tip === 'string' ? tip : tip.id
return this.tipIdSpriteMap.get(id)
}
public findOrCreate(tip: Tip): THREE.Sprite {
const sprite = this.find(tip)
if (sprite) return sprite
return this.create(tip)
}
public remove(tip: string | (Partial<Tip> & Pick<Tip, 'id'>)): void {
const id = typeof tip === 'string' ? tip : tip.id
const sprite = this.tipIdSpriteMap.get(id)
if (!sprite) return
this.parent.remove(sprite)
this.tipIdSpriteMap.delete(id)
}
public removeAll(): void {
this.tipIdSpriteMap.forEach(sprite => {
this.parent.remove(sprite)
})
this.tipIdSpriteMap.clear()
}
public destroy(): void {
this.removeAll()
this.removeAllListeners()
}
}
================================================
FILE: packages/vr360-core/src/types.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import type {ConfigModel} from './manager'
import type {SpaceManagerEvents} from './manager/space'
/**
* 完成跳转空间时的回调 event
*/
export type AfterSwitchSpaceEvent = {
/**
* 跳转的目标空间配置
*/
spaceConfig: SpaceConfig
}
/**
* vr360 暴露的自定义事件
*/
export type Vr360Events = {
/**
* 每帧更新回调
*/
update: () => void
/**
* 完成跳转空间时的回调
*/
afterSwitchSpace: (e: AfterSwitchSpaceEvent) => void
} & Omit<SpaceManagerEvents, 'switchSpace'>
/**
* 通用坐标
*/
export type Vector3Position = {
/**
* x 轴坐标
*/
x: number
/**
* y 轴坐标
*/
y: number
/**
* z 轴坐标
*/
z: number
}
/**
* 位置
*/
export type Position = Vector3Position
/**
* 缩放
*/
export type Scale = Vector3Position
/**
* 旋转
*/
export type Rotate = Vector3Position
/**
* 每个 threejs 对象的通用配置
*/
export type ThreeObjectBase = {
/**
* 位置
*/
position: Position
/**
* 缩放
*/
scale?: Scale
/**
* 旋转
*/
rotate?: Rotate
}
/**
* 立方体空间纹理贴图 url 列表
*/
export type CubeSpaceTextureUrls = {
/**
* 左侧贴图 url
*/
left: string
/**
* 右侧贴图 url
*/
right: string
/**
* 上侧贴图 url
*/
up: string
/**
* 下侧贴图 url
*/
down: string
/**
* 前侧贴图 url
*/
front: string
/**
* 后侧贴图 url
*/
back: string
}
/**
* 提示配置
*/
export type Tip = {
/**
* 提示 id
*/
id: string
/**
* 跳转的空间 id
*/
targetSpaceId?: string
/**
* 提示图案纹理贴图
*/
textureUrl?: string
/**
* 其它携带信息,比如你可以附加 title 、 content,在提示相关事件回调时你可以拿到
*/
[key: string]: any
} & ThreeObjectBase &
ConfigModel
/**
* 空间配置
*/
export type SpaceConfig = {
/**
* 空间 id
*/
id: string
/**
* 相机配置
*/
camera?: ThreeObjectBase
/**
* 提示配置列表
*/
tips?: Tip[]
/**
* 空间贴图列表
*/
cubeSpaceTextureUrls: CubeSpaceTextureUrls
} & ConfigModel
/**
* vr360 构造参数
*/
export type Vr360Options = {
/**
* 容器
*/
container: HTMLElement
/**
* 提示的 element 节点
*/
tipContainer?: HTMLElement
/**
* 初始显示的空间 id
*/
initSpaceId?: string
/**
* 空间配置
*/
spacesConfig: SpaceConfig[]
}
/**
* 深度处理 interface key,使所有 key 为必填项
*/
export type DeepRequired<T> = {
[P in keyof T]-?: T[P] extends (infer U)[] ? DeepRequired<U>[] : T[P] extends object ? DeepRequired<T[P]> : T[P]
}
/**
* 深度处理 interface key,使所有 key 为可选项
*/
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial<U>[] : T[P] extends object ? DeepPartial<T[P]> : T[P]
}
================================================
FILE: packages/vr360-core/src/vr360.ts
================================================
/* eslint-disable unicorn/no-array-callback-reference */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
import * as THREE from 'three'
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'
import TWEEN from '@tweenjs/tween.js'
import {EventEmitter} from 'eventemitter3'
import {
addListenerToThreeObject,
convertMousePositionToThreePosition,
formatBaseInfo,
get3dObjectBaseInfo,
TextureCacheLoader,
update3dObjectBaseInfo
} from './helper'
import type {Position, SpaceConfig, ThreeObjectBase, Vr360Events, Vr360Options} from './types'
import {spaceEventNames, SpaceManager} from './manager/space'
import type {SpaceEventName} from './manager/space'
export type Vr360EventName = keyof Vr360Events
/**
* 继承的空间管理器事件列表
*/
const extendsSpaceEventNames = spaceEventNames.filter(eventName => !['switchSpace'].includes(eventName)) as Exclude<
SpaceEventName,
'switchSpace'
>[]
/**
* vr360 的事件列表
*/
export const vr360EventNames: Vr360EventName[] = [...extendsSpaceEventNames, 'update', 'afterSwitchSpace']
export class Vr360 extends EventEmitter<Vr360Events> {
/**
* 销毁队列
*/
private destroyTasks: (() => void)[] = []
/**
* 当前空间 id
*/
private currentSpaceId = ''
/**
* 初始化空间 id
*/
public initSpaceId?: string
/**
* texture 缓存器
*/
private textureCacheLoader: TextureCacheLoader
/**
* 容器
*/
public container: HTMLElement
/**
* 相机
*/
public camera: THREE.PerspectiveCamera
/**
* 场景
*/
public scene: THREE.Scene
/**
* 渲染器
*/
public renderer: THREE.WebGLRenderer
/**
* 控制器
*/
public controls: OrbitControls
/**
* 提示容器
*/
public tipContainer?: HTMLElement
/**
* 空间管理器
*/
public spaceManager: SpaceManager
/**
* 空间配置
*/
public spacesConfig: SpaceConfig[]
/**
* 容器宽
*/
public get containerWidth() {
return this.container.clientWidth
}
/**
* 容器高
*/
public get containerHeight() {
return this.container.clientHeight
}
constructor(options: Vr360Options) {
super()
const {container, tipContainer, initSpaceId, spacesConfig = []} = options
this.textureCacheLoader = TextureCacheLoader.getInstance()
this.container = container
this.camera = this.createCamera()
this.scene = this.createScene()
this.renderer = this.createRenderer()
this.updateContainerSize()
this.controls = this.createControls()
this.container.append(this.renderer.domElement)
this.tipContainer = tipContainer
this.spacesConfig = [...spacesConfig]
this.initSpaceId = String(initSpaceId || '')
// 为 mesh 添加事件支持
addListenerToThreeObject(() => {
return {
camera: this.camera,
scene: this.scene,
renderer: this.renderer
}
})
// 缓存所有纹理
this.cacheAllTextures()
// 创建空间管理器
this.spaceManager = this.createSpaceManager()
// 实例化空间
this.updateSpacesConfig(this.spacesConfig)
// 初始化切换到第一个空间
// this.switchSpace(this.currentSpaceId)
}
/**
* 一次性加载并配置里所有的纹理图片
*/
public cacheAllTextures(): void {
const urls = this.spacesConfig.reduce<string[]>((urls, spaceConfig) => {
const {cubeSpaceTextureUrls} = spaceConfig
return [...urls, ...Object.values(cubeSpaceTextureUrls)]
}, [])
this.textureCacheLoader.loadUrls(urls)
}
/**
* 监听页面尺寸变化,更新画布尺寸
* @returns 返回取消监听函数
*/
public listenResize(): () => void {
const resizeFn = () => {
this.updateContainerSize()
}
window.addEventListener('resize', resizeFn)
const remove = () => {
window.removeEventListener('resize', resizeFn)
}
this.destroyTasks.push(remove)
return remove
}
/**
* 更新容器宽高
*/
public updateContainerSize(): void {
const width = this.containerWidth
const height = this.containerHeight
if (this.camera) {
this.camera.aspect = width / height
this.camera.updateProjectionMatrix()
}
if (this.renderer) {
this.renderer.setSize(width, height)
}
}
/**
* 更新 spacesConfig
* @param newSpacesConfig 新的空间配置
*/
public updateSpacesConfig(newSpacesConfig: SpaceConfig[]): void {
const oldSpacesConfig = this.spacesConfig
const newSpacesIds = new Set<string>()
newSpacesConfig.map(spaceConfig => {
// 如果空间已经存在,则更新空间配置,否则创建空间
this.spaceManager.update(spaceConfig) ?? this.spaceManager.create(spaceConfig)
newSpacesIds.add(String(spaceConfig.id))
})
oldSpacesConfig.map(spaceConfig => {
// 删掉旧的空间
if (!newSpacesIds.has(String(spaceConfig.id))) {
this.spaceManager.remove(String(spaceConfig.id))
}
})
this.spacesConfig = [...newSpacesConfig]
// 如果当前空间不存在,则切换到另一个空间
if (!this.currentSpaceId || !newSpacesIds.has(this.currentSpaceId)) {
// 默认空间 id
const defaultSpaceId = String(newSpacesConfig[0].id || '')
// 要跳转的空间 id
const switchSpaceId =
!this.currentSpaceId && this.initSpaceId ? this.initSpaceId || defaultSpaceId : defaultSpaceId
this.switchSpace(switchSpaceId)
}
}
/**
* 切换空间
* @param id 空间 id
* @param clickPosition 点击切换的位置,如果提高则会自动计算过渡动画
*/
public switchSpace(id: string, clickPosition?: THREE.Vector3): void {
const targetSpaceId = String(id)
// 相同的房间就不执行跳转了
if (this.currentSpaceId === targetSpaceId || !targetSpaceId) return
const spaceGroup = this.spaceManager.find(targetSpaceId)
// 找不到空间 id 相关的空间组,不执行跳转
if (!spaceGroup) return
// 找到空间组,但是挂载的信息丢失了,抛错
if (!spaceGroup.userData.spaceConfig) throw new Error('vr360: spaceConfig 不存在')
this.currentSpaceId = targetSpaceId
const spaceConfig = spaceGroup.userData.spaceConfig as SpaceConfig
const {camera} = spaceConfig
// 添加目标空间进场景
if (!this.scene.children.includes(spaceGroup)) {
this.scene.add(spaceGroup)
}
// 从场景里移除其它空间
this.scene.children.forEach(child => {
if (child instanceof THREE.Group && child.userData.type === 'spaceGroup' && child !== spaceGroup) {
this.scene.remove(child)
}
})
// 场景动画结束时,添加小标签和白点
const handleCompleteTween = () => {
// 触发切换空间完成事件
this.emit('afterSwitchSpace', {spaceConfig: spaceConfig})
}
// 下一个镜头的信息
const nextCameraInfo = formatBaseInfo(camera)
if (clickPosition) {
// 当前相机的信息
const currentCameraInfo: ThreeObjectBase = {
...get3dObjectBaseInfo(this.camera),
position: {
x: nextCameraInfo.position.x - (clickPosition.x - this.camera.position.x),
y: nextCameraInfo.position.y - (clickPosition.y - this.camera.position.y),
z: nextCameraInfo.position.z - (clickPosition.z - this.camera.position.z)
}
}
// 用 tween.js 添加镜头切换动画
new TWEEN.Tween(currentCameraInfo)
.to(nextCameraInfo, 500)
.onUpdate(cameraInfo => {
update3dObjectBaseInfo(this.camera, cameraInfo)
})
.easing(TWEEN.Easing.Linear.None)
.start()
.onComplete(() => {
handleCompleteTween()
})
} else {
update3dObjectBaseInfo(this.camera, nextCameraInfo)
handleCompleteTween()
}
}
/**
* 渲染
*/
public render(): void {
requestAnimationFrame(this.render.bind(this))
this.controls.update()
this.handleUpdate()
TWEEN.update()
}
/**
* 每次渲染更新时执行一些东西
*/
private handleUpdate(): void {
this.emit('update')
this.renderer.render(this.scene, this.camera)
}
/**
* 创建场景
* @returns 返回场景
*/
private createScene(): THREE.Scene {
const scene = new THREE.Scene()
this.destroyTasks.push(() => {
scene.remove(...scene.children)
})
return scene
}
/**
* 创建相机
* @returns 返回相机
*/
private createCamera(): THREE.PerspectiveCamera {
const camera = new THREE.PerspectiveCamera(75, this.containerWidth / this.containerHeight, 0.01, 10_000)
camera.position.set(0, 0, 0)
return camera
}
/**
* 创建 renderer 渲染器
* @returns 返回渲染器
*/
private createRenderer(): THREE.WebGLRenderer {
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
})
renderer.setClearColor(0xff_ff_ff, 0)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(this.containerWidth, this.containerHeight)
this.destroyTasks.push(() => {
renderer.dispose()
renderer.forceContextLoss()
renderer.domElement.remove()
})
return renderer
}
/**
* 创建控制器
* @returns 返回控制器
*/
private createControls(): OrbitControls {
const controls = new OrbitControls(this.camera, this.renderer.domElement)
controls.listenToKeyEvents(window)
controls.autoRotate = false
controls.autoRotateSpeed = 0.5
controls.enableZoom = false
controls.enableDamping = true
controls.enablePan = true
controls.enableRotate = true
controls.rotateSpeed = 0.5
controls.minDistance = 1
controls.maxDistance = 100
// controls.maxPolarAngle = Math.PI / 2
// controls.screenSpacePanning = false
// 控制器初始化位置为相机看到的位置
controls.target = new THREE.Vector3(0, 0, 1)
controls.update()
this.destroyTasks.push(() => {
controls.dispose()
})
return controls
}
/**
* 创建空间管理器
*/
private createSpaceManager(): SpaceManager {
const spaceManager = new SpaceManager({
container: this.container,
camera: this.camera,
scene: this.scene,
renderer: this.renderer,
tipContainer: this.tipContainer,
textureCacheLoader: this.textureCacheLoader,
parent: this.scene
})
// 空间事件继承
extendsSpaceEventNames.forEach(eventName => {
spaceManager.on(eventName, e => {
// @ts-ignore
this.emit(eventName, e)
})
})
// 特殊处理
spaceManager.on('switchSpace', e => {
this.switchSpace(e.targetSpaceId, e.clickPosition)
})
return spaceManager
}
/**
* 把鼠标 x、y坐标转换为画布内的三维坐标
* @param x 鼠标 x 坐标
* @param y 鼠标 y 坐标
* @returns 返回三维坐标
*/
public getPositionFromMouseXY(x: number, y: number): Position {
return convertMousePositionToThreePosition({
mouseX: x,
mouseY: y,
getDeps: () => ({
camera: this.camera,
renderer: this.renderer,
scene: this.scene
})
})
}
/**
* 销毁实例
*/
public destroy(): void {
this.destroyTasks.forEach(task => task())
this.removeAllListeners()
}
}
================================================
FILE: packages/vr360-core/test/index.ts
================================================
import {polyfillFetch} from '@nicepkg/vr360-shared/test-utils'
export * from '@nicepkg/vr360-shared/test-utils'
export * from '@nicepkg/vr360-shared/test-vue-utils'
polyfillFetch()
================================================
FILE: packages/vr360-core/test/setup.ts
================================================
import {polyfillFetch, polyfillPointerEvents} from '@nicepkg/vr360-shared/test-utils'
import {setupVueSwitch} from '@nicepkg/vr360-shared/test-vue-utils'
import {beforeAll, beforeEach} from 'vitest'
polyfillFetch()
polyfillPointerEvents()
setupVueSwitch()
beforeAll(() => {
setupVueSwitch()
})
beforeEach(() => {
document.body.innerHTML = ''
document.head.innerHTML = ''
})
================================================
FILE: packages/vr360-core/tsconfig.json
================================================
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist",
"types": ["vite/client", "vitest", "vitest/globals"],
"paths": {
"@/*": ["./src/*"],
"@test/*": ["./test/*"]
}
},
"include": ["./src/**/*", "./scripts/**/*", "./types/**/*", "./test/**/*", "./*.js", "./*.ts"],
"exclude": ["node_modules", "dist"]
}
================================================
FILE: packages/vr360-core/vite.config.ts
================================================
/// <reference types="vitest" />
import type {UserConfig} from 'vite'
import {defineConfig} from 'vite'
import {buildUtils} from '@nicepkg/vr360-shared'
export const packagePath = __dirname
type CreateViteConfigOptions = {
minify?: boolean
}
const createViteConfig = (options: CreateViteConfigOptions = {}): UserConfig => {
const {minify = false} = options
return buildUtils.createViteConfig({
packagePath,
minify,
externalMap: {
three: 'THREE'
},
dedupe: ['three'] // use the same version
})
}
// default config and build prod config
export const unMinifyConfig = createViteConfig({minify: false})
// build prod and build prod config
export const minifyConfig = createViteConfig({minify: true})
// for vitest
export default defineConfig(unMinifyConfig)
================================================
FILE: packages/vr360-shared/CHANGELOG.md
================================================
# 0.3.0 (2022-10-08)
### Bug Fixes
- **@nicepkg/vr360-core:** 修复移动端的提示移动 bug ([7090a98](https://github.com/nicepkg/vr360/commit/7090a9805bc472240de6ad4467bbadc14fd6151d))
# 0.3.0 (2022-10-02)
## 0.2.1 (2022-10-02)
# 0.2.0 (2022-10-02)
### Bug Fixes
- 修复 vr360-core tips 在滑动时的 bug ([e179ebc](https://github.com/nicepkg/vr360/commit/e179ebc2697314bc455320eecf8beb6182a53ded))
### Features
- 暴露更多的事件,设置默认自动旋转 ([4e21c53](https://github.com/nicepkg/vr360/commit/4e21c53ac945532020a7fbbfa46644294c33b49d))
- 初始化项目 ([06f39d1](https://github.com/nicepkg/vr360/commit/06f39d141004a1d0b1a125ad598298baf15ffee8))
- 添加标签提示和空间切换功能 ([6aa67d3](https://github.com/nicepkg/vr360/commit/6aa67d39113b06d05036ecc1d66ab1d70a2f4cf5))
- 添加如视 vr 导入支持 ([5a9eb5d](https://github.com/nicepkg/vr360/commit/5a9eb5d7a33d092be8cea5565490f268376d2f79))
- 文档添加示例插件、core 的纹理 top、bottom 改为 up 和 down ([70ccb2c](https://github.com/nicepkg/vr360/commit/70ccb2c40a06079ffb3cf50b27f986ab19b1f7db))
- 修改 package.json 说明 ([e5e4ad0](https://github.com/nicepkg/vr360/commit/e5e4ad04b1c7a9cddff3af6f73d438fd1de85b25))
- 优化代码 ([4519c4a](https://github.com/nicepkg/vr360/commit/4519c4a0fb230bb62bed49f97e0824c8977be3ca))
- 优化 sdk,添加纹理缓存预加载 ([9dd57e7](https://github.com/nicepkg/vr360/commit/9dd57e71b8f5a38ecc9901395a9b189481172edb))
- 重写核心,添加最小化更新配置支持 ([289091b](https://github.com/nicepkg/vr360/commit/289091b13dfe0495b44ae9e5353b78d2712e9762))
================================================
FILE: packages/vr360-shared/README.md
================================================
# Vr360-shared
内部实用功能的共享包,包括构建和测试功能。
================================================
FILE: packages/vr360-shared/build.config.ts
================================================
import {defineBuildConfig} from 'unbuild'
export default defineBuildConfig({
entries: ['src/index', 'src/test-utils', 'src/test-vue-utils', 'src/test-react-utils'],
clean: true,
declaration: true,
externals: ['vite', 'vitest', 'vue-demi', 'react'],
rollup: {
emitCJS: true,
cjsBridge: true
}
})
================================================
FILE: packages/vr360-shared/package.json
================================================
{
"name": "@nicepkg/vr360-shared",
"version": "0.3.1",
"description": "A shared package of internal utility functions, including build and test functions.",
"keywords": [
"vr360-shared",
"nicepkg",
"utils"
],
"author": "yangjinming <https://github.com/2214962083>",
"funding": "https://github.com/sponsors/2214962083",
"license": "MIT",
"private": false,
"scripts": {
"build": "pnpm type-check &&unbuild",
"build:watch": "pnpm build -- --stub",
"clean": "rimraf ./dist/**/*",
"type-check": "tsc --noEmit"
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
},
"./*": "./*",
"./test-utils": {
"require": "./dist/test-utils.cjs",
"import": "./dist/test-utils.mjs",
"types": "./dist/test-utils.d.ts"
},
"./test-vue-utils": {
"require": "./dist/test-vue-utils.cjs",
"import": "./dist/test-vue-utils.mjs",
"types": "./dist/test-vue-utils.d.ts"
},
"./test-react-utils": {
"require": "./dist/test-react-utils.cjs",
"import": "./dist/test-react-utils.mjs",
"types": "./dist/test-react-utils.d.ts"
}
},
"files": [
"dist"
],
"sideEffects": false,
"repository": {
"type": "git",
"url": "git+https://github.com/nicepkg/vr360.git",
"directory": "packages/vr360-shared"
},
"bugs": {
"url": "https://github.com/nicepkg/vr360/issues"
},
"homepage": "https://github.com/nicepkg/vr360#readme",
"peerDependencies": {
"vite": "*",
"vitest": "*",
"vue-demi": "*"
},
"peerDependenciesMeta": {
"vite": {
"optional": true
},
"vitest": {
"optional": true
},
"vue-demi": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-router-dom": {
"optional": true
},
"@testing-library/jest-dom": {
"optional": true
}
},
"dependencies": {
"@testing-library/react": "*",
"@testing-library/user-event": "^14.4.3",
"@types/fs-extra": "^9.0.13",
"@types/mockjs": "^1.0.7",
"@types/node": "*",
"@types/node-fetch": "^2.6.2",
"@types/rimraf": "^3.0.2",
"@vitejs/plugin-react": "^2.1.0",
"chalk": "4.1.2",
"fs-extra": "^10.1.0",
"globby": "11.1.0",
"mockjs": "^1.1.0",
"msw": "^0.39.2",
"node-fetch": "2.6.7",
"ora": "5.4.1",
"rimraf": "^3.0.2",
"rollup-plugin-visualizer": "^5.8.2",
"unplugin-auto-import": "^0.11.2",
"vite-plugin-dts": "^1.6.1",
"vite-plugin-inspect": "0.6.1",
"vite-plugin-mock": "^2.9.6",
"vite-plugin-svgr": "^2.2.1",
"vite-tsconfig-paths": "^3.5.1",
"vitest-fetch-mock": "0.2.1"
},
"devDependencies": {
"@testing-library/dom": "*",
"@testing-library/jest-dom": "*",
"@types/react": "*",
"@types/react-dom": "*",
"@types/react-router-dom": "*",
"autoprefixer": "^10.4.12",
"conventional-changelog-cli": "*",
"cross-env": "^7.0.3",
"cssnano": "^5.1.13",
"esno": "*",
"postcss": "^8.4.16",
"react": "*",
"react-dom": "*",
"react-router-dom": "*",
"rollup": "^2.79.1",
"typescript": "*",
"unbuild": "^0.8.11",
"vite": "*",
"vitest": "*",
"vue": "^3.2.40",
"vue-demi": "*",
"vue2": "npm:vue@2.6.14"
}
}
================================================
FILE: packages/vr360-shared/src/build-utils/build-script.util.ts
================================================
import type {InlineConfig, UserConfig, BuildOptions as ViteBuildOptions} from 'vite'
import {build as viteBuild} from 'vite'
import rimraf from 'rimraf'
import path from 'node:path'
import dts from 'vite-plugin-dts'
import type {PluginVisualizerOptions} f
gitextract_4oa5dsxh/ ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ └── feature_request.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── commit-convention.md │ ├── settings.yml │ └── workflows/ │ ├── docs.yml │ ├── lint.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .husky/ │ ├── commit-msg │ ├── pre-commit │ └── prepare-commit-msg ├── .npmrc ├── .stylelintignore ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── lint-staged.config.js ├── netlify.toml ├── package.json ├── packages/ │ ├── doc/ │ │ ├── .vuepress/ │ │ │ ├── client.ts │ │ │ ├── components/ │ │ │ │ ├── DemoA.vue │ │ │ │ └── NpmBadge.vue │ │ │ ├── configs/ │ │ │ │ ├── index.ts │ │ │ │ ├── navbar/ │ │ │ │ │ ├── en.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── zh.ts │ │ │ │ └── sidebar/ │ │ │ │ ├── en.ts │ │ │ │ ├── index.ts │ │ │ │ └── zh.ts │ │ │ ├── examples/ │ │ │ │ └── firstHouse/ │ │ │ │ ├── demo.css │ │ │ │ ├── html.html │ │ │ │ ├── react.tsx │ │ │ │ ├── vue2.vue │ │ │ │ └── vue3.vue │ │ │ ├── index.build.html │ │ │ ├── plugins/ │ │ │ │ ├── code-demo/ │ │ │ │ │ ├── CodeDemo.props.ts │ │ │ │ │ ├── CodeDemo.vue │ │ │ │ │ ├── Icons.tsx │ │ │ │ │ ├── clientConfigFile.ts │ │ │ │ │ ├── constant.ts │ │ │ │ │ ├── global.d.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── plugin.ts │ │ │ │ └── index.ts │ │ │ ├── public/ │ │ │ │ ├── browserconfig.xml │ │ │ │ ├── code-demo-templates/ │ │ │ │ │ ├── demo.css.txt │ │ │ │ │ ├── html/ │ │ │ │ │ │ └── package.json.txt │ │ │ │ │ ├── react/ │ │ │ │ │ │ ├── index.html.txt │ │ │ │ │ │ ├── package.json.txt │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ └── main.tsx.txt │ │ │ │ │ │ ├── tsconfig.json.txt │ │ │ │ │ │ └── vite.config.ts.txt │ │ │ │ │ ├── vue2/ │ │ │ │ │ │ ├── index.html.txt │ │ │ │ │ │ ├── package.json.txt │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ ├── App.vue.txt │ │ │ │ │ │ │ └── main.ts.txt │ │ │ │ │ │ ├── tsconfig.json.txt │ │ │ │ │ │ ├── types/ │ │ │ │ │ │ │ └── module.d.ts.txt │ │ │ │ │ │ └── vite.config.ts.txt │ │ │ │ │ └── vue3/ │ │ │ │ │ ├── index.html.txt │ │ │ │ │ ├── package.json.txt │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── App.vue.txt │ │ │ │ │ │ └── main.ts.txt │ │ │ │ │ ├── tsconfig.json.txt │ │ │ │ │ ├── types/ │ │ │ │ │ │ └── module.d.ts.txt │ │ │ │ │ └── vite.config.ts.txt │ │ │ │ └── manifest.webmanifest │ │ │ ├── styles/ │ │ │ │ └── index.scss │ │ │ ├── theme/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Home.vue │ │ │ │ │ ├── HomeFeatures.vue │ │ │ │ │ └── HomeVrBg.vue │ │ │ │ └── index.ts │ │ │ ├── types/ │ │ │ │ └── module.d.ts │ │ │ └── utils/ │ │ │ └── common.ts │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── bundler.config.ts │ │ ├── guide/ │ │ │ ├── README.md │ │ │ └── questions.md │ │ ├── libs/ │ │ │ ├── vr360-core/ │ │ │ │ ├── README.md │ │ │ │ ├── events.md │ │ │ │ ├── example.md │ │ │ │ ├── methods.md │ │ │ │ └── properties.md │ │ │ ├── vr360-ui/ │ │ │ │ └── README.md │ │ │ ├── vr360-ui-react/ │ │ │ │ └── README.md │ │ │ ├── vr360-ui-vue2/ │ │ │ │ └── README.md │ │ │ └── vr360-ui-vue3/ │ │ │ └── README.md │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── vuepress.config.ts │ ├── vr360-core/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── package.json │ │ ├── scripts/ │ │ │ └── build.ts │ │ ├── src/ │ │ │ ├── helper.ts │ │ │ ├── index.ts │ │ │ ├── manager/ │ │ │ │ ├── index.ts │ │ │ │ ├── space.ts │ │ │ │ └── tip.ts │ │ │ ├── types.ts │ │ │ └── vr360.ts │ │ ├── test/ │ │ │ ├── index.ts │ │ │ └── setup.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── vr360-shared/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── build.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── build-utils/ │ │ │ │ ├── build-script.util.ts │ │ │ │ ├── index.ts │ │ │ │ └── vite-config-common.util.ts │ │ │ ├── index.ts │ │ │ ├── test-react-utils.ts │ │ │ ├── test-utils/ │ │ │ │ ├── common/ │ │ │ │ │ ├── helper.util.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── mock-global-api.ts │ │ │ │ │ ├── mock-server.util.ts │ │ │ │ │ ├── polyfill-fetch.util.ts │ │ │ │ │ └── polyfill-pointer-events.util.ts │ │ │ │ ├── react/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── react-helper.util.ts │ │ │ │ │ └── react-mount.util.ts │ │ │ │ └── vue/ │ │ │ │ ├── index.ts │ │ │ │ ├── vue-helper.util.ts │ │ │ │ └── vue-mount.util.ts │ │ │ ├── test-utils.ts │ │ │ └── test-vue-utils.ts │ │ ├── test-react-utils.d.ts │ │ ├── test-utils.d.ts │ │ ├── test-vue-utils.d.ts │ │ ├── tsconfig.json │ │ └── types/ │ │ └── global.d.ts │ ├── vr360-ui/ │ │ └── README.md │ ├── vr360-ui-react/ │ │ └── README.md │ ├── vr360-ui-vue2/ │ │ └── README.md │ └── vr360-ui-vue3/ │ └── README.md ├── playgrounds/ │ ├── react/ │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── Example.tsx │ │ │ └── main.tsx │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── vue2/ │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.vue │ │ │ └── main.ts │ │ ├── tsconfig.json │ │ ├── types/ │ │ │ └── module.d.ts │ │ ├── unocss.config.ts │ │ └── vite.config.ts │ └── vue3/ │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.vue │ │ ├── ContextMenu.vue │ │ ├── Editor.vue │ │ ├── EditorHotPointManager.vue │ │ ├── EditorLeftBar.vue │ │ ├── EditorSceneManager.vue │ │ ├── EditorSettings.vue │ │ ├── EditorTipsManager.vue │ │ ├── EditorTopBar.vue │ │ ├── Icons.tsx │ │ ├── helper.ts │ │ ├── main.ts │ │ └── useVr360.ts │ ├── tsconfig.json │ ├── types/ │ │ └── module.d.ts │ ├── unocss.config.ts │ └── vite.config.ts ├── pnpm-workspace.yaml ├── prettier.config.js ├── scripts/ │ ├── build.ts │ ├── check-update.ts │ ├── release.ts │ └── utils.ts ├── stylelint.config.js ├── textures.json ├── tsconfig-base.json ├── tsconfig.json └── turbo.json
SYMBOL INDEX (150 symbols across 29 files)
FILE: packages/doc/.vuepress/client.ts
method enhance (line 7) | enhance({app}) {
FILE: packages/doc/.vuepress/examples/firstHouse/react.tsx
function Example (line 6) | function Example() {
FILE: packages/doc/.vuepress/plugins/code-demo/CodeDemo.props.ts
type CodeDemoProps (line 26) | type CodeDemoProps = Partial<ExtractPropTypes<ReturnType<typeof getProps>>>
FILE: packages/doc/.vuepress/plugins/code-demo/Icons.tsx
method render (line 8) | render() {
FILE: packages/doc/.vuepress/plugins/code-demo/clientConfigFile.ts
method enhance (line 9) | async enhance({app}) {
FILE: packages/doc/.vuepress/plugins/code-demo/constant.ts
constant DEFAULT_EDITOR_TITLE (line 2) | const DEFAULT_EDITOR_TITLE = 'Vr360 Example'
constant DEFAULT_EDITOR_DESCRIPTION (line 5) | const DEFAULT_EDITOR_DESCRIPTION = ''
constant DEFAULT_VR360_VERSION (line 8) | const DEFAULT_VR360_VERSION = '^6.0.0'
FILE: packages/doc/.vuepress/plugins/code-demo/global.d.ts
type Window (line 6) | interface Window {
FILE: packages/doc/.vuepress/plugins/code-demo/plugin.ts
type MarkdownItRenderFn (line 16) | type MarkdownItRenderFn = (
type CodeDemoOptions (line 24) | type CodeDemoOptions = {
type RenderPlaceFunction (line 30) | type RenderPlaceFunction = (description: string, codeBlockTokens?: Token...
FILE: packages/vr360-core/src/helper.ts
function getSingleValue (line 9) | function getSingleValue() {
type ConvertMousePositionToThreePositionOptions (line 18) | type ConvertMousePositionToThreePositionOptions = {
function convertMousePositionToThreePosition (line 40) | function convertMousePositionToThreePosition(options: ConvertMousePositi...
type GetAddListenerToThreeObjectDeps (line 60) | type GetAddListenerToThreeObjectDeps = () => {
type ThreeObjectDispatchEvent (line 77) | type ThreeObjectDispatchEvent<T extends keyof HTMLElementEventMap> = {
function addListenerToThreeObject (line 89) | function addListenerToThreeObject(
class TextureCacheLoader (line 183) | class TextureCacheLoader {
method getInstance (line 185) | static getInstance() {
method loadUrl (line 205) | loadUrl(url: string) {
method loadUrls (line 217) | loadUrls(urls: string[]) {
function hasOwnProperty (line 228) | function hasOwnProperty(obj: any, key: string): boolean {
function isEqual (line 238) | function isEqual(a: any, b: any): boolean {
function debounce (line 269) | function debounce<T extends (...args: any[]) => any>(func: T, wait: numb...
function update3dObjectBaseInfo (line 291) | function update3dObjectBaseInfo(object: THREE.Object3D, baseInfo: Partia...
function get3dObjectBaseInfo (line 311) | function get3dObjectBaseInfo(object?: THREE.Object3D): ThreeObjectBase {
function formatBaseInfo (line 336) | function formatBaseInfo(baseInfo: DeepPartial<ThreeObjectBase> | undefin...
FILE: packages/vr360-core/src/manager/index.ts
type ConfigModel (line 4) | type ConfigModel = {
type ConfigModelManager (line 8) | type ConfigModelManager<CM extends ConfigModel, TM> = {
FILE: packages/vr360-core/src/manager/space.ts
type SpaceManagerEvents (line 14) | type SpaceManagerEvents = TipManagerEvents
type SpaceEventName (line 16) | type SpaceEventName = keyof SpaceManagerEvents
type SpaceManagerOptions (line 19) | type SpaceManagerOptions = Omit<TipManagerOptions, 'tipContainer'> & {
class SpaceManager (line 26) | class SpaceManager
method constructor (line 41) | constructor(options: SpaceManagerOptions) {
method createTipManager (line 57) | private createTipManager(parent: THREE.Object3D): TipManager | undefin...
method createCubeSpaceMesh (line 85) | private createCubeSpaceMesh(cubeSpaceTextureUrls: CubeSpaceTextureUrls) {
method createCubeSpaceMaterials (line 102) | private createCubeSpaceMaterials(cubeSpaceTextureUrls: CubeSpaceTextur...
method create (line 113) | public create(spaceConfig: SpaceConfig): THREE.Group {
method add (line 141) | public add(spaceConfig: SpaceConfig): THREE.Group {
method update (line 147) | public update(spaceConfig: Partial<SpaceConfig> & Pick<SpaceConfig, 'i...
method find (line 199) | public find(spaceConfig: string | (Partial<SpaceConfig> & Pick<SpaceCo...
method findOrCreate (line 204) | public findOrCreate(spaceConfig: SpaceConfig): THREE.Group {
method remove (line 210) | public remove(spaceConfig: string | (Partial<SpaceConfig> & Pick<Space...
method removeAll (line 220) | public removeAll(): void {
method destroy (line 233) | public destroy(): void {
FILE: packages/vr360-core/src/manager/tip.ts
type ShowTipEvent (line 12) | type ShowTipEvent = {
type HideTipEvent (line 32) | type HideTipEvent = {
type ClickTipEvent (line 39) | type ClickTipEvent = {
type SwitchSpaceEvent (line 46) | type SwitchSpaceEvent = {
type TipManagerEvents (line 61) | type TipManagerEvents = {
type TipEventName (line 83) | type TipEventName = keyof TipManagerEvents
type TipManagerOptions (line 86) | type TipManagerOptions = {
class TipManager (line 126) | class TipManager extends EventEmitter<TipManagerEvents> implements Confi...
method constructor (line 137) | constructor(options: TipManagerOptions) {
method create (line 148) | public create(tip: Tip): THREE.Sprite {
method add (line 247) | public add(tip: Tip): THREE.Sprite {
method update (line 253) | public update(tip: Partial<Tip> & Pick<Tip, 'id'>): THREE.Sprite | und...
method find (line 277) | public find(tip: string | (Partial<Tip> & Pick<Tip, 'id'>)): THREE.Spr...
method findOrCreate (line 282) | public findOrCreate(tip: Tip): THREE.Sprite {
method remove (line 288) | public remove(tip: string | (Partial<Tip> & Pick<Tip, 'id'>)): void {
method removeAll (line 297) | public removeAll(): void {
method destroy (line 304) | public destroy(): void {
FILE: packages/vr360-core/src/types.ts
type AfterSwitchSpaceEvent (line 9) | type AfterSwitchSpaceEvent = {
type Vr360Events (line 19) | type Vr360Events = {
type Vector3Position (line 34) | type Vector3Position = {
type Position (line 54) | type Position = Vector3Position
type Scale (line 59) | type Scale = Vector3Position
type Rotate (line 64) | type Rotate = Vector3Position
type ThreeObjectBase (line 69) | type ThreeObjectBase = {
type CubeSpaceTextureUrls (line 89) | type CubeSpaceTextureUrls = {
type Tip (line 124) | type Tip = {
type SpaceConfig (line 150) | type SpaceConfig = {
type Vr360Options (line 175) | type Vr360Options = {
type DeepRequired (line 200) | type DeepRequired<T> = {
type DeepPartial (line 207) | type DeepPartial<T> = {
FILE: packages/vr360-core/src/vr360.ts
type Vr360EventName (line 21) | type Vr360EventName = keyof Vr360Events
class Vr360 (line 36) | class Vr360 extends EventEmitter<Vr360Events> {
method containerWidth (line 100) | public get containerWidth() {
method containerHeight (line 107) | public get containerHeight() {
method constructor (line 111) | constructor(options: Vr360Options) {
method cacheAllTextures (line 153) | public cacheAllTextures(): void {
method listenResize (line 165) | public listenResize(): () => void {
method updateContainerSize (line 182) | public updateContainerSize(): void {
method updateSpacesConfig (line 200) | public updateSpacesConfig(newSpacesConfig: SpaceConfig[]): void {
method switchSpace (line 237) | public switchSpace(id: string, clickPosition?: THREE.Vector3): void {
method render (line 308) | public render(): void {
method handleUpdate (line 318) | private handleUpdate(): void {
method createScene (line 327) | private createScene(): THREE.Scene {
method createCamera (line 341) | private createCamera(): THREE.PerspectiveCamera {
method createRenderer (line 352) | private createRenderer(): THREE.WebGLRenderer {
method createControls (line 374) | private createControls(): OrbitControls {
method createSpaceManager (line 403) | private createSpaceManager(): SpaceManager {
method getPositionFromMouseXY (line 436) | public getPositionFromMouseXY(x: number, y: number): Position {
method destroy (line 451) | public destroy(): void {
FILE: packages/vr360-core/vite.config.ts
type CreateViteConfigOptions (line 8) | type CreateViteConfigOptions = {
FILE: packages/vr360-shared/src/build-utils/build-script.util.ts
constant WATCH (line 9) | const WATCH = Boolean(process.env.WATCH)
constant REPORT (line 10) | const REPORT = Boolean(process.env.REPORT)
type DtsOptions (line 12) | type DtsOptions = Parameters<typeof dts>[0]
type GetItemType (line 14) | type GetItemType<T> = T extends (infer U)[] ? U : T
type RollupOutput (line 15) | type RollupOutput = NonNullable<NonNullable<ViteBuildOptions['rollupOpti...
type OutputOptions (line 16) | type OutputOptions = GetItemType<RollupOutput>
type ChangeConfigOptions (line 18) | type ChangeConfigOptions = {
function changeViteConfig (line 27) | function changeViteConfig(config: UserConfig, options: ChangeConfigOptio...
type ChangeConfigFn (line 74) | type ChangeConfigFn = (config: UserConfig, options: ChangeConfigOptions)...
type BuildOptions (line 75) | type BuildOptions = {
function build (line 84) | async function build(config: BuildOptions) {
FILE: packages/vr360-shared/src/build-utils/vite-config-common.util.ts
type CreateViteConfigOptions (line 23) | type CreateViteConfigOptions = {
FILE: packages/vr360-shared/src/test-utils/common/mock-global-api.ts
function mockGlobalApi (line 1) | function mockGlobalApi() {
FILE: packages/vr360-shared/src/test-utils/common/polyfill-pointer-events.util.ts
class PointerEvent (line 6) | class PointerEvent extends MouseEvent {
method constructor (line 9) | constructor(type: string, params: PointerEventInit = {}) {
FILE: packages/vr360-shared/src/test-utils/react/react-helper.util.ts
function commonSetup (line 4) | function commonSetup(options?: {title?: string}) {
FILE: packages/vr360-shared/src/test-utils/react/react-mount.util.ts
function mount (line 2) | function mount() {
FILE: packages/vr360-shared/src/test-utils/vue/vue-helper.util.ts
function setProps (line 13) | function setProps(props: Record<string, any>) {
function createFunctionComponent (line 28) | function createFunctionComponent(fn: (...args: any[]) => any) {
function getSlots (line 42) | function getSlots(ctx: any, slotName: string) {
function setupVueSwitch (line 50) | function setupVueSwitch() {
FILE: packages/vr360-shared/src/test-utils/vue/vue-mount.util.ts
type InstanceType (line 6) | type InstanceType<V> = V extends new (...arg: any[]) => infer X ? X : never
type VM (line 7) | type VM<V> = InstanceType<V> & {unmount(): void}
function find (line 9) | function find(selector: string, target?: HTMLElement | null | undefined) {
type SnapType (line 23) | type SnapType = 'all' | 'vue2' | 'vue3'
type MountResult (line 25) | type MountResult<V> = VM<V> & {
function mount (line 30) | function mount<V extends Component>(Comp: V): MountResult<V> {
function getComponent (line 67) | function getComponent(tagName: string) {
function useSetup (line 71) | function useSetup<V>(setup: () => V) {
function useInjectedSetup (line 84) | function useInjectedSetup<V>(setup: () => V) {
FILE: packages/vr360-shared/types/global.d.ts
type Writable (line 4) | type Writable<T> = {
type DeepPartial (line 8) | type DeepPartial<T> = {
type TimeoutHandle (line 12) | type TimeoutHandle = ReturnType<typeof setTimeout>
type IntervalHandle (line 13) | type IntervalHandle = ReturnType<typeof setInterval>
FILE: playgrounds/react/src/Example.tsx
function Example (line 1) | function Example() {
FILE: playgrounds/vue3/src/helper.ts
type HttpOptions (line 6) | type HttpOptions = {
type DeepPartial (line 140) | type DeepPartial<T> = {
type RealseeVrInfo (line 148) | type RealseeVrInfo = {
type RealseeVrInfoInitial (line 166) | type RealseeVrInfoInitial = {
type RealseeVrInfoModel (line 174) | type RealseeVrInfoModel = {
type RealseeVrInfoObserver (line 182) | type RealseeVrInfoObserver = {
type RealseeVrInfoObserverQuaternion (line 193) | type RealseeVrInfoObserverQuaternion = {
type RealseeVrInfoPanorama (line 200) | type RealseeVrInfoPanorama = {
type RealseeVrInfoPanoramaList (line 205) | type RealseeVrInfoPanoramaList = {
FILE: playgrounds/vue3/src/useVr360.ts
type MaybeRef (line 8) | type MaybeRef<T> = T | Ref<T>
type UseVr360Options (line 10) | type UseVr360Options = {
function useVr360 (line 70) | function useVr360(options: UseVr360Options) {
FILE: scripts/check-update.ts
function checkNpmPkgUpdate (line 5) | async function checkNpmPkgUpdate() {
FILE: scripts/utils.ts
function copyFiles (line 13) | function copyFiles() {
type PackageInfo (line 26) | type PackageInfo = {path: string; name: string}
function getPackagesInfo (line 27) | function getPackagesInfo(type: 'public' | 'all'): PackageInfo[] {
function build (line 56) | function build() {
function generateChangelog (line 62) | function generateChangelog() {
function release (line 75) | function release() {
Condensed preview — 193 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (287K chars).
[
{
"path": ".editorconfig",
"chars": 147,
"preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_"
},
{
"path": ".eslintignore",
"chars": 230,
"preview": "**/node_modules/\n*.html\nes/\n**/lib/\n**/dist/\n**/lib-types/\n_site/\n**/dist/\nCHANGELOG.md\n.rollup.cache\ntsconfig.tsbuildin"
},
{
"path": ".eslintrc.js",
"chars": 7316,
"preview": "//@ts-check\n\nmodule.exports = /** @type { import('eslint').Linter.Config } */ ({\n root: true,\n extends: [\n 'eslint:"
},
{
"path": ".github/FUNDING.yml",
"chars": 21,
"preview": "github: [2214962083]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 2655,
"preview": "name: \"\\U0001F41E Bug report\"\ndescription: Report an issue with Vr360\nbody:\n - type: markdown\n attributes:\n val"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 461,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Question\n url: https://github.com/nicepkg/vr360/discussions/new?"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 1797,
"preview": "name: \"\\U0001F680 New feature proposal\"\ndescription: Propose a new feature to be added to Vr360\nlabels: ['feature reques"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 924,
"preview": "<!-- Thank you for contributing! -->\n\n### Description\n\n<!-- Please insert your description here and provide especially i"
},
{
"path": ".github/commit-convention.md",
"chars": 2930,
"preview": "## Git Commit Message Convention\n\n> This is adapted from [Angular's commit convention](https://github.com/conventional-c"
},
{
"path": ".github/settings.yml",
"chars": 474,
"preview": "labels:\n - name: bug\n color: ee0701\n - name: contribution welcome\n color: 0e8a16\n - name: discussion\n color:"
},
{
"path": ".github/workflows/docs.yml",
"chars": 2305,
"preview": "name: Docs\n\non:\n push:\n branches:\n - main\n - master\n\n pull_request:\n branches:\n - main\n - ma"
},
{
"path": ".github/workflows/lint.yml",
"chars": 746,
"preview": "name: Lint\n\non:\n push:\n branches:\n - main\n - master\n\n pull_request:\n branches:\n - main\n - ma"
},
{
"path": ".github/workflows/release.yml",
"chars": 1321,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\njobs:\n release:\n runs-on: ubuntu-latest\n env:\n TURBO_TOKE"
},
{
"path": ".github/workflows/test.yml",
"chars": 1117,
"preview": "name: Test\n\non:\n push:\n branches:\n - main\n - master\n\n pull_request:\n branches:\n - main\n - ma"
},
{
"path": ".gitignore",
"chars": 514,
"preview": ".DS_Store\n.vite-ssg-dist\n.vite-ssg-temp\n*.local\ndist\ndist-ssr\nnode_modules\n.idea/\n*.log\nstats.html\n.vite-inspect\n.histor"
},
{
"path": ".husky/commit-msg",
"chars": 104,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\n# lint commit message\nnpx --no-install commitlint --edit \"$1\"\n"
},
{
"path": ".husky/pre-commit",
"chars": 94,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\n# lint git stash files\nnpx --no-install lint-staged\n"
},
{
"path": ".husky/prepare-commit-msg",
"chars": 245,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\necho \"do not 'git commit' please run 'pnpm commit' instead\"\n# echo \"commit lin"
},
{
"path": ".npmrc",
"chars": 1462,
"preview": "# registry = \"https://registry.npmmirror.com\"\n\nsass_binary_site = \"https://cdn.npmmirror.com/binaries/node-sass\"\nphantom"
},
{
"path": ".stylelintignore",
"chars": 122,
"preview": "node_modules/\n**/*.spec.*\nes/\nlib/\n_site/\ndist/\n**/node_modules/*\n**/segi-ant-theme.less\n**/.rollup.cache/*\n**/index.htm"
},
{
"path": ".vscode/extensions.json",
"chars": 325,
"preview": "{\n \"recommendations\": [\n \"antfu.iconify\",\n \"antfu.unocss\",\n \"antfu.goto-alias\",\n \"csstools.postcss\",\n \"d"
},
{
"path": ".vscode/settings.json",
"chars": 2840,
"preview": "{\n // Use PNPM\n \"npm.packageManager\": \"pnpm\",\n // \"eslint.packageManager\": \"pnpm\",\n \"typescript.tsdk\": \"./node_modul"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3352,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 3428,
"preview": "# Contributing\n\nThanks for being interested in contributing to this project!\n\n## Development\n\nTo improve our development"
},
{
"path": "LICENSE",
"chars": 1100,
"preview": "MIT License\n\nCopyright (c) 2021 YangJinMing <https://github.com/2214962083>\n\nPermission is hereby granted, free of charg"
},
{
"path": "README.md",
"chars": 3308,
"preview": "<div align=\"center\">\n <a href=\"https://vr360.nicepkg.cn/\">\n <img src=\"https://vr360.nicepkg.cn/images/logo-bg.png\" w"
},
{
"path": "commitlint.config.js",
"chars": 68,
"preview": "module.exports = {\n extends: ['@commitlint/config-conventional']\n}\n"
},
{
"path": "lint-staged.config.js",
"chars": 296,
"preview": "module.exports = {\n '*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],\n '*.json': ['prettier --write'],\n '*.v"
},
{
"path": "netlify.toml",
"chars": 193,
"preview": "[build.environment]\n NODE_VERSION = \"16\"\n NPM_FLAGS = \"--version\" # prevent Netlify npm install\n[build]\n publish = \"p"
},
{
"path": "package.json",
"chars": 4858,
"preview": "{\n \"name\": \"@nicepkg/vr360\",\n \"version\": \"0.3.1\",\n \"private\": true,\n \"packageManager\": \"pnpm@7.0.0\",\n \"author\": \"ya"
},
{
"path": "packages/doc/.vuepress/client.ts",
"chars": 3707,
"preview": "/* eslint-disable unicorn/no-await-expression-member */\nimport type {ProjectFiles} from '@stackblitz/sdk'\nimport {define"
},
{
"path": "packages/doc/.vuepress/components/DemoA.vue",
"chars": 4221,
"preview": "<template>\n <div class=\"demoA\">\n <div\n ref=\"tipRef\"\n class=\"demoA-tip\"\n :style=\"{\n transform: "
},
{
"path": "packages/doc/.vuepress/components/NpmBadge.vue",
"chars": 831,
"preview": "<script setup lang=\"ts\">\nimport {computed} from 'vue'\n\nconst props = defineProps({\n package: {\n type: String,\n re"
},
{
"path": "packages/doc/.vuepress/configs/index.ts",
"chars": 72,
"preview": "export * as navbar from './navbar'\nexport * as sidebar from './sidebar'\n"
},
{
"path": "packages/doc/.vuepress/configs/navbar/en.ts",
"chars": 344,
"preview": "import type {NavbarConfig} from '@vuepress/theme-default'\nimport {version} from '../../utils/common'\n\nexport const en: N"
},
{
"path": "packages/doc/.vuepress/configs/navbar/index.ts",
"chars": 42,
"preview": "export * from './en'\nexport * from './zh'\n"
},
{
"path": "packages/doc/.vuepress/configs/navbar/zh.ts",
"chars": 469,
"preview": "import type {NavbarConfig} from '@vuepress/theme-default'\nimport {version} from '../../utils/common'\n\nexport const zh: N"
},
{
"path": "packages/doc/.vuepress/configs/sidebar/en.ts",
"chars": 218,
"preview": "import type {SidebarConfig} from '@vuepress/theme-default'\n\nexport const en: SidebarConfig = {\n '/en/guide/': [\n {\n "
},
{
"path": "packages/doc/.vuepress/configs/sidebar/index.ts",
"chars": 42,
"preview": "export * from './en'\nexport * from './zh'\n"
},
{
"path": "packages/doc/.vuepress/configs/sidebar/zh.ts",
"chars": 495,
"preview": "import type {SidebarConfig} from '@vuepress/theme-default'\n\nexport const zh: SidebarConfig = {\n '/guide/': [\n {\n "
},
{
"path": "packages/doc/.vuepress/examples/firstHouse/demo.css",
"chars": 886,
"preview": "* {\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n}\n\n.demo {\n position: relative;\n display: flex;\n flex-direct"
},
{
"path": "packages/doc/.vuepress/examples/firstHouse/html.html",
"chars": 6427,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n <head>\n <meta charset=\"UTF-8\" />\n <title>Html App</title>\n <meta\n na"
},
{
"path": "packages/doc/.vuepress/examples/firstHouse/react.tsx",
"chars": 5887,
"preview": "import React, {useEffect, useState, useRef} from 'react'\nimport {Vr360} from '@nicepkg/vr360-core'\nimport type {SpaceCon"
},
{
"path": "packages/doc/.vuepress/examples/firstHouse/vue2.vue",
"chars": 5699,
"preview": "<template>\n <div class=\"demo\">\n <!-- 提示 -->\n <div\n ref=\"tipRef\"\n class=\"demo-tip\"\n :style=\"{\n "
},
{
"path": "packages/doc/.vuepress/examples/firstHouse/vue3.vue",
"chars": 5245,
"preview": "<template>\n <div class=\"demo\">\n <!-- 提示 -->\n <div\n ref=\"tipRef\"\n class=\"demo-tip\"\n :style=\"{\n "
},
{
"path": "packages/doc/.vuepress/index.build.html",
"chars": 454,
"preview": "<!DOCTYPE html>\n<html lang=\"{{ lang }}\">\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=de"
},
{
"path": "packages/doc/.vuepress/plugins/code-demo/CodeDemo.props.ts",
"chars": 510,
"preview": "import type {Project} from '@stackblitz/sdk'\nimport type {ExtractPropTypes, PropType} from 'vue'\n\nexport const getProps "
},
{
"path": "packages/doc/.vuepress/plugins/code-demo/CodeDemo.vue",
"chars": 2697,
"preview": "<template>\n <div\n ref=\"codeDemoRef\"\n class=\"code-demo\"\n :style=\"{\n border: isFullScreen ? 'none' : '1px s"
},
{
"path": "packages/doc/.vuepress/plugins/code-demo/Icons.tsx",
"chars": 1494,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {VNode} from 'vue'\nimport {defineComponent, h} from "
},
{
"path": "packages/doc/.vuepress/plugins/code-demo/clientConfigFile.ts",
"chars": 1616,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {CodeDemoProps} from './CodeDemo.props'\nimport {defi"
},
{
"path": "packages/doc/.vuepress/plugins/code-demo/constant.ts",
"chars": 240,
"preview": "// 用于 Stackblitz 示例的默认标题(未覆盖时)\nexport const DEFAULT_EDITOR_TITLE = 'Vr360 Example'\n\n// 用于 Stackblitz 示例的默认描述(未覆盖时)\nexpor"
},
{
"path": "packages/doc/.vuepress/plugins/code-demo/global.d.ts",
"chars": 385,
"preview": "import type {ProjectFiles} from '@stackblitz/sdk'\nimport type {CodeDemoProps} from './CodeDemo.props'\n\ndeclare global {\n"
},
{
"path": "packages/doc/.vuepress/plugins/code-demo/index.ts",
"chars": 25,
"preview": "export * from './plugin'\n"
},
{
"path": "packages/doc/.vuepress/plugins/code-demo/plugin.ts",
"chars": 4219,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport typ"
},
{
"path": "packages/doc/.vuepress/plugins/index.ts",
"chars": 942,
"preview": "import type {PluginConfig} from 'vuepress'\nimport {registerComponentsPlugin} from '@vuepress/plugin-register-components'"
},
{
"path": "packages/doc/.vuepress/public/browserconfig.xml",
"chars": 259,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n <msapplication>\n <tile>\n <square150x150logo"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/demo.css.txt",
"chars": 838,
"preview": "* {\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n}\n\n.demo {\n position: relative;\n display: flex;\n flex-direct"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/html/package.json.txt",
"chars": 167,
"preview": "{\n \"name\": \"playground-html\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"http-server ./ -p 8000 -o\"\n },\n \"devDepen"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/react/index.html.txt",
"chars": 852,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" href=\"/favicon.ico\" />\n <title>R"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/react/package.json.txt",
"chars": 545,
"preview": "{\n \"name\": \"playground-react\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"vite\",\n \"build\": \"vite build\",\n \"ser"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/react/src/main.tsx.txt",
"chars": 204,
"preview": "import React from 'react'\nimport ReactDOM from 'react-dom'\nimport Example from './Example'\n\nReactDOM.render(\n <React.St"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/react/tsconfig.json.txt",
"chars": 561,
"preview": "{\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"target\": \"esnext\",\n \"composite\": true,\n "
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/react/vite.config.ts.txt",
"chars": 331,
"preview": "import {defineConfig} from 'vite'\nimport path from 'node:path'\nimport viteReact from '@vitejs/plugin-react'\n\nconst pathR"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue2/index.html.txt",
"chars": 850,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" href=\"/favicon.ico\" />\n <title>V"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue2/package.json.txt",
"chars": 497,
"preview": "{\n \"name\": \"playground-vue2\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"vite\",\n \"build\": \"vite build\",\n \"serv"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue2/src/App.vue.txt",
"chars": 178,
"preview": "<template>\n <Example></Example>\n</template>\n\n<script lang=\"ts\">\nimport Example from './Example.vue'\n\nexport default {\n "
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue2/src/main.ts.txt",
"chars": 135,
"preview": "import Vue from 'vue'\nimport App from './App.vue'\n\nVue.config.productionTip = false\n\nnew Vue({\n render: h => h(App)\n})."
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue2/tsconfig.json.txt",
"chars": 560,
"preview": "{\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"target\": \"esnext\",\n \"composite\": true,\n "
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue2/types/module.d.ts.txt",
"chars": 130,
"preview": "declare module '*.vue' {\n import type {VueConstructor} from 'vue'\n const component: VueConstructor\n export default co"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue2/vite.config.ts.txt",
"chars": 420,
"preview": "import path from 'node:path'\nimport {defineConfig} from 'vite'\nimport {createVuePlugin} from 'vite-plugin-vue2'\n\nconst p"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue3/index.html.txt",
"chars": 850,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" href=\"/favicon.ico\" />\n <title>V"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue3/package.json.txt",
"chars": 466,
"preview": "{\n \"name\": \"playground-vue3\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"vite\",\n \"build\": \"vite build\",\n \"serv"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue3/src/App.vue.txt",
"chars": 117,
"preview": "<template>\n <Example></Example>\n</template>\n\n<script setup lang=\"ts\">\nimport Example from './Example.vue'\n</script>\n"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue3/src/main.ts.txt",
"chars": 155,
"preview": "import {createApp} from 'vue'\nimport App from './App.vue'\n\nconst app = createApp(App)\napp.mount('#app')\n\napp.config.glob"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue3/tsconfig.json.txt",
"chars": 560,
"preview": "{\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"target\": \"esnext\",\n \"composite\": true,\n "
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue3/types/module.d.ts.txt",
"chars": 151,
"preview": "declare module '*.vue' {\n import type {DefineComponent} from 'vue-demi'\n const Component: DefineComponent<{}, {}, any>"
},
{
"path": "packages/doc/.vuepress/public/code-demo-templates/vue3/vite.config.ts.txt",
"chars": 406,
"preview": "import path from 'node:path'\nimport {defineConfig} from 'vite'\nimport Vue from '@vitejs/plugin-vue'\nimport vueJsx from '"
},
{
"path": "packages/doc/.vuepress/public/manifest.webmanifest",
"chars": 1375,
"preview": "{\n \"name\": \"vr360\",\n \"short_name\": \"vr360\",\n \"description\": \"快速实现你的全景开发需求\",\n \"theme_color\": \"#ffffff\",\n \"background"
},
{
"path": "packages/doc/.vuepress/styles/index.scss",
"chars": 797,
"preview": ":root {\n // brand colors\n --c-brand: #0c4dc4;\n --c-brand-light: #0b3788;\n\n scroll-behavior: smooth;\n}\n\nhtml.dark {\n "
},
{
"path": "packages/doc/.vuepress/theme/components/Home.vue",
"chars": 611,
"preview": "<script setup lang=\"ts\">\nimport HomeContent from '@vuepress/theme-default/components/HomeContent.vue'\nimport HomeFeature"
},
{
"path": "packages/doc/.vuepress/theme/components/HomeFeatures.vue",
"chars": 1404,
"preview": "<script setup lang=\"ts\">\nimport {usePageFrontmatter} from '@vuepress/client'\nimport {isArray} from '@vuepress/shared'\nim"
},
{
"path": "packages/doc/.vuepress/theme/components/HomeVrBg.vue",
"chars": 3445,
"preview": "<template>\n <div class=\"vr-container-wrapper\" :style=\"{height: height + 'px'}\">\n <div\n ref=\"tipRef\"\n class"
},
{
"path": "packages/doc/.vuepress/theme/index.ts",
"chars": 2350,
"preview": "import type {Theme} from '@vuepress/core'\nimport {defaultTheme} from '@vuepress/theme-default'\nimport {path} from '@vuep"
},
{
"path": "packages/doc/.vuepress/types/module.d.ts",
"chars": 261,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\n\ndeclare module '*.vue' {\n import type {DefineComponent} from '"
},
{
"path": "packages/doc/.vuepress/utils/common.ts",
"chars": 157,
"preview": "import rootPkg from '../../../../package.json'\n\nexport const version = rootPkg.version as string\nexport const isProd = p"
},
{
"path": "packages/doc/CHANGELOG.md",
"chars": 1410,
"preview": "# 0.3.0 (2022-10-08)\n\n### Bug Fixes\n\n- **@nicepkg/vr360-core:** 修复移动端的提示移动 bug ([7090a98](https://github.com/nicepkg/vr3"
},
{
"path": "packages/doc/README.md",
"chars": 969,
"preview": "---\nhome: true\ntitle: 首页\nheroImage: /images/logo.png\nheroImageDark: /images/logo-dark.png\nheroText: null\ntagline: 快速实现你的"
},
{
"path": "packages/doc/bundler.config.ts",
"chars": 456,
"preview": "import {path} from '@vuepress/utils'\nimport type {ViteBundlerOptions} from 'vuepress'\n\nconst pathResolve = (..._path: st"
},
{
"path": "packages/doc/guide/README.md",
"chars": 711,
"preview": "# 介绍\n\nVr360 是一个基于 threejs 能让你快速实现业务全景需求的库,比如全景看房、全景街景、全景景点。\n\n它的核心被设计为框架无关性,可以用 json 配置的方式快速实现常见全景需求。\n\n后续还会提供高度封装的 viewer"
},
{
"path": "packages/doc/guide/questions.md",
"chars": 127,
"preview": "# 常见问题\n\n## vr360-ui 为什么没有 angular 版\n\nvr360-ui 是基于 stencil.js 开发,适配 angular 是很容易的,但是由于本人没怎么用过 angular,适配完也不知道有没有 bug,所以欢迎"
},
{
"path": "packages/doc/libs/vr360-core/README.md",
"chars": 2540,
"preview": "# 介绍\n\n`@nicepkg/vr360-core` 是一个基于 [threejs](https://github.com/mrdoob/three.js/) 的全景库,非常适合用来做全景看房、全景街景、全景景点等业务需求。\n\n它支持 j"
},
{
"path": "packages/doc/libs/vr360-core/events.md",
"chars": 1943,
"preview": "# 事件\n\n## 显示提示\n\n#### 介绍\n\n```ts\ninterface Vr360Events {\n /**\n * 触发提示时的回调\n */\n showTip: (e: {\n /**\n * 提示配置信息\n "
},
{
"path": "packages/doc/libs/vr360-core/example.md",
"chars": 541,
"preview": "# 示例\n\n## 效果\n\n<br/>\n<DemoA></DemoA>\n\n### vue2 实现\n\n::: demo vue2 -- vue2 示例代码 -- 这里是示例代码的描述\n\n```vue src/Example.vue\n/*# @/"
},
{
"path": "packages/doc/libs/vr360-core/methods.md",
"chars": 5154,
"preview": "# 方法\n\n## 构造器\n\n#### 介绍\n\n```ts\nclass Vr360 {\n /**\n * @param options 构造参数配置\n * @returns Vr360 实例\n */\n constructor(o"
},
{
"path": "packages/doc/libs/vr360-core/properties.md",
"chars": 1909,
"preview": "# 属性\n\n## 全景容器\n\n##### 介绍\n\n```ts\nclass Vr360 {\n /**\n * 容器\n */\n public container: HTMLElement\n}\n```\n\n##### 使用\n\n```ts\n"
},
{
"path": "packages/doc/libs/vr360-ui/README.md",
"chars": 77,
"preview": "# 介绍\n\n(开发中...)提供一个现成的 vr360 viewer 和 editor 组件,基于 stencil 构建的 web component。\n"
},
{
"path": "packages/doc/libs/vr360-ui-react/README.md",
"chars": 61,
"preview": "# 介绍\n\n(开发中...)提供一个现成的 vr360 viewer 和 editor 组件,适配 react 框架版。\n"
},
{
"path": "packages/doc/libs/vr360-ui-vue2/README.md",
"chars": 60,
"preview": "# 介绍\n\n(开发中...)提供一个现成的 vr360 viewer 和 editor 组件,适配 vue2 框架版。\n"
},
{
"path": "packages/doc/libs/vr360-ui-vue3/README.md",
"chars": 60,
"preview": "# 介绍\n\n(开发中...)提供一个现成的 vr360 viewer 和 editor 组件,适配 vue3 框架版。\n"
},
{
"path": "packages/doc/package.json",
"chars": 1312,
"preview": "{\n \"name\": \"doc\",\n \"version\": \"0.3.1\",\n \"private\": true,\n \"description\": \"the libs docs website\",\n \"scripts\": {\n "
},
{
"path": "packages/doc/tsconfig.json",
"chars": 346,
"preview": "{\n \"extends\": \"../../tsconfig-base.json\",\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"type"
},
{
"path": "packages/doc/vuepress.config.ts",
"chars": 2044,
"preview": "import {defineUserConfig} from 'vuepress'\nimport {path} from '@vuepress/utils'\nimport {viteBundler} from '@vuepress/bund"
},
{
"path": "packages/vr360-core/CHANGELOG.md",
"chars": 1410,
"preview": "# 0.3.0 (2022-10-08)\n\n### Bug Fixes\n\n- **@nicepkg/vr360-core:** 修复移动端的提示移动 bug ([7090a98](https://github.com/nicepkg/vr3"
},
{
"path": "packages/vr360-core/README.md",
"chars": 1972,
"preview": "<div align=\"center\">\n <a href=\"https://vr360.nicepkg.cn/libs/vr360-core/\">\n <img src=\"https://vr360.nicepkg.cn/image"
},
{
"path": "packages/vr360-core/package.json",
"chars": 1807,
"preview": "{\n \"name\": \"@nicepkg/vr360-core\",\n \"version\": \"0.3.1\",\n \"description\": \"快速实现你的全景开发需求,全景看房、全景街景、全景景点\",\n \"keywords\": ["
},
{
"path": "packages/vr360-core/scripts/build.ts",
"chars": 265,
"preview": "import {buildUtils} from '@nicepkg/vr360-shared'\nimport {minifyConfig, unMinifyConfig, packagePath} from '../vite.config"
},
{
"path": "packages/vr360-core/src/helper.ts",
"chars": 9628,
"preview": "/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n"
},
{
"path": "packages/vr360-core/src/index.ts",
"chars": 74,
"preview": "export * from './vr360'\nexport * from './types'\nexport * from './manager'\n"
},
{
"path": "packages/vr360-core/src/manager/index.ts",
"chars": 501,
"preview": "export * from './space'\nexport * from './tip'\n\nexport type ConfigModel = {\n id: string\n}\n\nexport type ConfigModelManage"
},
{
"path": "packages/vr360-core/src/manager/space.ts",
"chars": 6989,
"preview": "/* eslint-disable @typescript-eslint/ban-ts-comment */\nimport * as THREE from 'three'\nimport type {TextureCacheLoader} f"
},
{
"path": "packages/vr360-core/src/manager/tip.ts",
"chars": 7689,
"preview": "import * as THREE from 'three'\nimport type {TextureCacheLoader, ThreeObjectDispatchEvent} from '../helper'\nimport {updat"
},
{
"path": "packages/vr360-core/src/types.ts",
"chars": 2524,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type {ConfigModel} from './manager'\nimport type {SpaceMa"
},
{
"path": "packages/vr360-core/src/vr360.ts",
"chars": 10593,
"preview": "/* eslint-disable unicorn/no-array-callback-reference */\n/* eslint-disable @typescript-eslint/ban-ts-comment */\n/* eslin"
},
{
"path": "packages/vr360-core/test/index.ts",
"chars": 182,
"preview": "import {polyfillFetch} from '@nicepkg/vr360-shared/test-utils'\nexport * from '@nicepkg/vr360-shared/test-utils'\nexport *"
},
{
"path": "packages/vr360-core/test/setup.ts",
"chars": 384,
"preview": "import {polyfillFetch, polyfillPointerEvents} from '@nicepkg/vr360-shared/test-utils'\nimport {setupVueSwitch} from '@nic"
},
{
"path": "packages/vr360-core/tsconfig.json",
"chars": 389,
"preview": "{\n \"extends\": \"../../tsconfig-base.json\",\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"type"
},
{
"path": "packages/vr360-core/vite.config.ts",
"chars": 794,
"preview": "/// <reference types=\"vitest\" />\nimport type {UserConfig} from 'vite'\nimport {defineConfig} from 'vite'\nimport {buildUti"
},
{
"path": "packages/vr360-shared/CHANGELOG.md",
"chars": 1410,
"preview": "# 0.3.0 (2022-10-08)\n\n### Bug Fixes\n\n- **@nicepkg/vr360-core:** 修复移动端的提示移动 bug ([7090a98](https://github.com/nicepkg/vr3"
},
{
"path": "packages/vr360-shared/README.md",
"chars": 38,
"preview": "# Vr360-shared\n\n内部实用功能的共享包,包括构建和测试功能。\n"
},
{
"path": "packages/vr360-shared/build.config.ts",
"chars": 316,
"preview": "import {defineBuildConfig} from 'unbuild'\n\nexport default defineBuildConfig({\n entries: ['src/index', 'src/test-utils',"
},
{
"path": "packages/vr360-shared/package.json",
"chars": 3484,
"preview": "{\n \"name\": \"@nicepkg/vr360-shared\",\n \"version\": \"0.3.1\",\n \"description\": \"A shared package of internal utility functi"
},
{
"path": "packages/vr360-shared/src/build-utils/build-script.util.ts",
"chars": 3232,
"preview": "import type {InlineConfig, UserConfig, BuildOptions as ViteBuildOptions} from 'vite'\nimport {build as viteBuild} from 'v"
},
{
"path": "packages/vr360-shared/src/build-utils/index.ts",
"chars": 78,
"preview": "export * from './build-script.util'\nexport * from './vite-config-common.util'\n"
},
{
"path": "packages/vr360-shared/src/build-utils/vite-config-common.util.ts",
"chars": 3359,
"preview": "/* eslint-disable @typescript-eslint/no-var-requires */\n/// <reference types=\"vitest\" />\nimport type {InlineConfig, Libr"
},
{
"path": "packages/vr360-shared/src/index.ts",
"chars": 787,
"preview": "/* eslint-disable unicorn/prefer-export-from */\n\nimport chalk from 'chalk'\nimport * as msw from 'msw'\nimport * as globby"
},
{
"path": "packages/vr360-shared/src/test-react-utils.ts",
"chars": 71,
"preview": "// this file will build as a bundle\nexport * from './test-utils/react'\n"
},
{
"path": "packages/vr360-shared/src/test-utils/common/helper.util.ts",
"chars": 666,
"preview": "// Like `until` but works off of any assertion, not application code.\nexport const retry = (assertion: () => void, {inte"
},
{
"path": "packages/vr360-shared/src/test-utils/common/index.ts",
"chars": 186,
"preview": "// this file will build as a bundle\nexport * from './helper.util'\nexport * from './mock-server.util'\nexport * from './po"
},
{
"path": "packages/vr360-shared/src/test-utils/common/mock-global-api.ts",
"chars": 320,
"preview": "export function mockGlobalApi() {\n // Mock matchMedia\n vi.stubGlobal('matchMedia', (query: string) => ({\n matches: "
},
{
"path": "packages/vr360-shared/src/test-utils/common/mock-server.util.ts",
"chars": 2454,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Network mocking with MSW.\n * Import this helper into the "
},
{
"path": "packages/vr360-shared/src/test-utils/common/polyfill-fetch.util.ts",
"chars": 133,
"preview": "import nodeFetch from 'node-fetch'\n\nexport const polyfillFetch = () => {\n // @ts-expect-error override\n window.fetch ="
},
{
"path": "packages/vr360-shared/src/test-utils/common/polyfill-pointer-events.util.ts",
"chars": 552,
"preview": "export const polyfillPointerEvents = () => {\n /* eslint-disable @typescript-eslint/no-explicit-any */\n // polyfill for"
},
{
"path": "packages/vr360-shared/src/test-utils/react/index.ts",
"chars": 338,
"preview": "/* eslint-disable unicorn/prefer-export-from */\n// this file will build as a bundle\n// all of react utils\nimport * as te"
},
{
"path": "packages/vr360-shared/src/test-utils/react/react-helper.util.ts",
"chars": 290,
"preview": "import {mockGlobalApi} from '../common/mock-global-api'\nimport createFetchMock from 'vitest-fetch-mock'\n\nexport function"
},
{
"path": "packages/vr360-shared/src/test-utils/react/react-mount.util.ts",
"chars": 107,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nexport function mount() {\n console.log('mount')\n}\n"
},
{
"path": "packages/vr360-shared/src/test-utils/vue/index.ts",
"chars": 128,
"preview": "// this file will build as a bundle\n// all of vue-demi utils\nexport * from './vue-helper.util'\nexport * from './vue-moun"
},
{
"path": "packages/vr360-shared/src/test-utils/vue/vue-helper.util.ts",
"chars": 1477,
"preview": "/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n"
},
{
"path": "packages/vr360-shared/src/test-utils/vue/vue-mount.util.ts",
"chars": 2534,
"preview": "/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n"
},
{
"path": "packages/vr360-shared/src/test-utils.ts",
"chars": 72,
"preview": "// this file will build as a bundle\nexport * from './test-utils/common'\n"
},
{
"path": "packages/vr360-shared/src/test-vue-utils.ts",
"chars": 69,
"preview": "// this file will build as a bundle\nexport * from './test-utils/vue'\n"
},
{
"path": "packages/vr360-shared/test-react-utils.d.ts",
"chars": 40,
"preview": "export * from './dist/test-react-utils'\n"
},
{
"path": "packages/vr360-shared/test-utils.d.ts",
"chars": 34,
"preview": "export * from './dist/test-utils'\n"
},
{
"path": "packages/vr360-shared/test-vue-utils.d.ts",
"chars": 38,
"preview": "export * from './dist/test-vue-utils'\n"
},
{
"path": "packages/vr360-shared/tsconfig.json",
"chars": 389,
"preview": "{\n \"extends\": \"../../tsconfig-base.json\",\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"type"
},
{
"path": "packages/vr360-shared/types/global.d.ts",
"chars": 449,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\n\ndeclare global {\n type Writable<T> = {\n -readonly [P in key"
},
{
"path": "packages/vr360-ui/README.md",
"chars": 6,
"preview": "# 开发中\n"
},
{
"path": "packages/vr360-ui-react/README.md",
"chars": 6,
"preview": "# 开发中\n"
},
{
"path": "packages/vr360-ui-vue2/README.md",
"chars": 6,
"preview": "# 开发中\n"
},
{
"path": "packages/vr360-ui-vue3/README.md",
"chars": 6,
"preview": "# 开发中\n"
},
{
"path": "playgrounds/react/index.html",
"chars": 852,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" href=\"/favicon.ico\" />\n <title>R"
},
{
"path": "playgrounds/react/package.json",
"chars": 688,
"preview": "{\n \"name\": \"playground-react\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"concurrently -r -k -g 'npm:build:deps' 'np"
},
{
"path": "playgrounds/react/src/Example.tsx",
"chars": 99,
"preview": "function Example() {\n return <div className=\"bg-gray-100\">Example</div>\n}\n\nexport default Example\n"
},
{
"path": "playgrounds/react/src/main.tsx",
"chars": 258,
"preview": "import React from 'react'\nimport ReactDOM from 'react-dom'\nimport Example from './Example'\n\nimport '@unocss/reset/tailwi"
},
{
"path": "playgrounds/react/tsconfig.json",
"chars": 354,
"preview": "{\n \"extends\": \"../../tsconfig-base.json\",\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"jsx\""
},
{
"path": "playgrounds/react/vite.config.ts",
"chars": 441,
"preview": "import {defineConfig} from 'vite'\nimport path from 'node:path'\nimport viteReact from '@vitejs/plugin-react'\nimport viteU"
},
{
"path": "playgrounds/vue2/index.html",
"chars": 850,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" href=\"/favicon.ico\" />\n <title>V"
},
{
"path": "playgrounds/vue2/package.json",
"chars": 717,
"preview": "{\n \"name\": \"playground-vue2\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"concurrently -r -k -g 'npm:build:deps' 'npm"
},
{
"path": "playgrounds/vue2/src/App.vue",
"chars": 3538,
"preview": "<template>\n <div class=\"relative w-100vw h-100vh overflow-hidden flex flex-col\">\n <div\n ref=\"tipRef\"\n clas"
},
{
"path": "playgrounds/vue2/src/main.ts",
"chars": 277,
"preview": "import Vue from 'vue'\nimport App from './App.vue'\nimport VueCompositionAPI from '@vue/composition-api'\n\n// css\nimport 'u"
},
{
"path": "playgrounds/vue2/tsconfig.json",
"chars": 493,
"preview": "{\n \"extends\": \"../../tsconfig-base.json\",\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"type"
},
{
"path": "playgrounds/vue2/types/module.d.ts",
"chars": 274,
"preview": "declare module '*.vue' {\n import type {VueConstructor} from 'vue'\n const component: VueConstructor\n export default co"
},
{
"path": "playgrounds/vue2/unocss.config.ts",
"chars": 791,
"preview": "import {\n defineConfig,\n presetAttributify,\n presetIcons,\n presetUno,\n transformerDirectives,\n transformerVariantG"
},
{
"path": "playgrounds/vue2/vite.config.ts",
"chars": 973,
"preview": "import path from 'node:path'\nimport {defineConfig} from 'vite'\nimport {createVuePlugin} from 'vite-plugin-vue2'\nimport S"
},
{
"path": "playgrounds/vue3/index.html",
"chars": 850,
"preview": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" href=\"/favicon.ico\" />\n <title>V"
},
{
"path": "playgrounds/vue3/package.json",
"chars": 669,
"preview": "{\n \"name\": \"playground-vue3\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"concurrently -r -k -g 'npm:build:deps' 'npm"
},
{
"path": "playgrounds/vue3/src/App.vue",
"chars": 3297,
"preview": "<template>\n <div class=\"w-100vw h-100vh\">\n <Editor>\n <div class=\"relative w-full h-full overflow-hidden flex fl"
},
{
"path": "playgrounds/vue3/src/ContextMenu.vue",
"chars": 2146,
"preview": "<template>\n <div\n v-show=\"showContextMenu\"\n ref=\"contextMenuRef\"\n class=\"vr360-context-menu absolute z-999 tex"
},
{
"path": "playgrounds/vue3/src/Editor.vue",
"chars": 918,
"preview": "<template>\n <div class=\"vr360-editor relative w-full h-full overflow-hidden flex flex-col\">\n <div class=\"vr360-edito"
},
{
"path": "playgrounds/vue3/src/EditorHotPointManager.vue",
"chars": 161,
"preview": "<template>\n <div class=\"editor-hot-point-manager w-full h-full flex flex-col\">传送白点</div>\n</template>\n\n<script setup lan"
},
{
"path": "playgrounds/vue3/src/EditorLeftBar.vue",
"chars": 1476,
"preview": "<template>\n <div class=\"vr360-editor-left-bar w-full h-full flex\">\n <div class=\"vr360-editor-left-bar-menu-list h-fu"
},
{
"path": "playgrounds/vue3/src/EditorSceneManager.vue",
"chars": 2566,
"preview": "<template>\n <div class=\"editor-scene-manager w-full h-full flex flex-col\">\n <input\n v-model=\"realseeVrUrl\"\n "
},
{
"path": "playgrounds/vue3/src/EditorSettings.vue",
"chars": 150,
"preview": "<template>\n <div class=\"editor-settings w-full h-full flex flex-col\">设置</div>\n</template>\n\n<script setup lang=\"ts\"></sc"
},
{
"path": "playgrounds/vue3/src/EditorTipsManager.vue",
"chars": 156,
"preview": "<template>\n <div class=\"editor-tips-manager w-full h-full flex flex-col\">文本标签</div>\n</template>\n\n<script setup lang=\"ts"
},
{
"path": "playgrounds/vue3/src/EditorTopBar.vue",
"chars": 215,
"preview": "<template>\n <div class=\"vr360-editor-top-bar w-full h-full flex items-center\">\n <span class=\"text-2xl font-bold ml-8"
},
{
"path": "playgrounds/vue3/src/Icons.tsx",
"chars": 1475,
"preview": "/* eslint-disable react/no-unknown-property */\nexport const DeleteIcon = () => (\n <svg xmlns=\"http://www.w3.org/2000/sv"
},
{
"path": "playgrounds/vue3/src/helper.ts",
"chars": 5865,
"preview": "/* eslint-disable unicorn/prefer-add-event-listener */\nimport type {CubeSpaceTextureUrls} from '@nicepkg/vr360-core'\nimp"
},
{
"path": "playgrounds/vue3/src/main.ts",
"chars": 216,
"preview": "import {createApp} from 'vue'\nimport App from './App.vue'\n\n// css\nimport 'uno.css'\nimport '@unocss/reset/tailwind.css'\n\n"
},
{
"path": "playgrounds/vue3/src/useVr360.ts",
"chars": 2580,
"preview": "/* eslint-disable @typescript-eslint/no-unnecessary-type-arguments */\n/* eslint-disable @typescript-eslint/no-unsafe-mem"
},
{
"path": "playgrounds/vue3/tsconfig.json",
"chars": 315,
"preview": "{\n \"extends\": \"../../tsconfig-base.json\",\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist\",\n \"type"
},
{
"path": "playgrounds/vue3/types/module.d.ts",
"chars": 409,
"preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\ndeclare module '*.vue' {\n import type {DefineComponent} from 'v"
},
{
"path": "playgrounds/vue3/unocss.config.ts",
"chars": 791,
"preview": "import {\n defineConfig,\n presetAttributify,\n presetIcons,\n presetUno,\n transformerDirectives,\n transformerVariantG"
},
{
"path": "playgrounds/vue3/vite.config.ts",
"chars": 1037,
"preview": "import path from 'node:path'\nimport type {UserConfig} from 'vite'\nimport {defineConfig, loadEnv} from 'vite'\nimport Vue "
},
{
"path": "pnpm-workspace.yaml",
"chars": 85,
"preview": "packages:\n - 'packages/**'\n - 'examples/**'\n - 'playgrounds/**'\n - '!**/test/**'\n"
},
{
"path": "prettier.config.js",
"chars": 296,
"preview": "// @ts-check\n\nmodule.exports = /** @type { import ('prettier').RequiredOptions } */ ({\n printWidth: 120,\n semi: false"
},
{
"path": "scripts/build.ts",
"chars": 63,
"preview": "import {build, copyFiles} from './utils'\n\ncopyFiles()\n\nbuild()\n"
},
{
"path": "scripts/check-update.ts",
"chars": 536,
"preview": "import * as ncu from 'npm-check-updates'\nimport fs from 'node:fs'\nimport {packagesPaths, pathResolve} from './utils'\n\nas"
},
{
"path": "scripts/release.ts",
"chars": 43,
"preview": "import {release} from './utils'\n\nrelease()\n"
},
{
"path": "scripts/utils.ts",
"chars": 2520,
"preview": "import {copyFileSync, readFileSync, existsSync} from 'node:fs'\nimport {execSync} from 'node:child_process'\nimport path f"
},
{
"path": "stylelint.config.js",
"chars": 2180,
"preview": "//@ts-check\n\nmodule.exports = /** @type { Partial<import('stylelint').Config> } */ ({\n customSyntax: 'postcss-less',\n "
},
{
"path": "textures.json",
"chars": 3648,
"preview": "{\n \"hotpot\": \"https://m.360buyimg.com/babel/jfs/t1/125314/12/31594/6260/6339b149E14068522/5c0d35a3e149936a.png\",\n \"bei"
},
{
"path": "tsconfig-base.json",
"chars": 1044,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es6\",\n \"module\": \"esnext\",\n \"typeRoots\": [\"./node_modules/@types\", \"types\""
},
{
"path": "tsconfig.json",
"chars": 236,
"preview": "{\n \"extends\": \"./tsconfig-base.json\",\n \"compilerOptions\": {\n \"baseUrl\": \"./\"\n },\n \"include\": [\"./scripts/**/*\", \""
},
{
"path": "turbo.json",
"chars": 708,
"preview": "{\n \"$schema\": \"https://turborepo.org/schema.json\",\n \"baseBranch\": \"origin/master\",\n \"pipeline\": {\n \"@nicepkg/vr360"
}
]
About this extraction
This page contains the full source code of the nicepkg/vr360 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 193 files (241.0 KB), approximately 79.8k tokens, and a symbol index with 150 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.