Commit f1a978d3 authored by lihaochen's avatar lihaochen

🎉 initial commit

parents
node_modules
dist/
.DS_Store
\ No newline at end of file
node_modules
docs/.vuepress/dist
<div align="center">
<h1>快应用开发者文档</h1>
</div>
<div align="center">
<a href="https://github.com/prettier/prettier">
<img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square" alt="prettier">
</a>
<a href="https://nodejs.org/en/">
<img src="https://img.shields.io/badge/node-%3E=%208.0.0-green.svg" alt="node version">
</a>
</div>
## 目标与理念
此为基于 [VuePress](https://vuepress.vuejs.org/zh/) 所搭建的「快应用」开发者文档。
## 先决条件
[Node.js](https://nodejs.org/en/) (>= 8.*), Npm 版本 5+ (更推荐使用 [Yarn](https://yarnpkg.com/zh-Hans/docs/install#mac-stable)).
## 如何使用
```bash
# 统一安装依赖
yarn
yarn start
```
## 如何提交
```bash
# 安装
npm i -g gitmoji-cli
# 使用
gitmoji -i
git add .
git commit
```
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "vuepress",
"short_name": "奇美拉文档",
"start_url": ".",
"display": "standalone",
"background_color": "#fff",
"theme_color": "#3eaf7c",
"icons": [
{
"src": "/quickapp.ico",
"sizes": "120x120",
"type": "image/png"
}
]
}
$accentColor = #4761f6
\ No newline at end of file
The MIT License (MIT)
Copyright (c) 2018-present, Yuxi (Evan) You
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.
<template>
<form
id="search-form"
class="algolia-search-wrapper search-box"
role="search"
>
<input
id="algolia-search-input"
class="search-query"
:placeholder="placeholder"
>
</form>
</template>
<script>
export default {
props: ['options'],
data () {
return {
placeholder: undefined
}
},
mounted () {
this.initialize(this.options, this.$lang)
this.placeholder = this.$site.themeConfig.searchPlaceholder || ''
},
methods: {
initialize (userOptions, lang) {
Promise.all([
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
]).then(([docsearch]) => {
docsearch = docsearch.default
const { algoliaOptions = {}} = userOptions
docsearch(Object.assign(
{},
userOptions,
{
inputSelector: '#algolia-search-input',
// #697 Make docsearch work well at i18n mode.
algoliaOptions: Object.assign({
'facetFilters': [`lang:${lang}`].concat(algoliaOptions.facetFilters || [])
}, algoliaOptions),
handleSelected: (input, event, suggestion) => {
const { pathname, hash } = new URL(suggestion.url)
this.$router.push(`${pathname}${hash}`)
}
}
))
})
},
update (options, lang) {
this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
this.initialize(options, lang)
}
},
watch: {
$lang (newValue) {
this.update(this.options, newValue)
},
options (newValue) {
this.update(newValue, this.$lang)
}
}
}
</script>
<style lang="stylus">
.algolia-search-wrapper
& > span
vertical-align middle
.algolia-autocomplete
line-height normal
.ds-dropdown-menu
background-color #fff
border 1px solid #999
border-radius 4px
font-size 16px
margin 6px 0 0
padding 4px
text-align left
&:before
border-color #999
[class*=ds-dataset-]
border none
padding 0
.ds-suggestions
margin-top 0
.ds-suggestion
border-bottom 1px solid $borderColor
.algolia-docsearch-suggestion--highlight
color #2c815b
.algolia-docsearch-suggestion
border-color $borderColor
padding 0
.algolia-docsearch-suggestion--category-header
padding 5px 10px
margin-top 0
background $accentColor
color #fff
font-weight 600
.algolia-docsearch-suggestion--highlight
background rgba(255, 255, 255, 0.6)
.algolia-docsearch-suggestion--wrapper
padding 0
.algolia-docsearch-suggestion--title
font-weight 600
margin-bottom 0
color $textColor
.algolia-docsearch-suggestion--subcategory-column
vertical-align top
padding 5px 7px 5px 5px
border-color $borderColor
background #f1f3f5
&:after
display none
.algolia-docsearch-suggestion--subcategory-column-text
color #555
.algolia-docsearch-footer
border-color $borderColor
.ds-cursor .algolia-docsearch-suggestion--content
background-color #e7edf3 !important
color $textColor
@media (min-width: $MQMobile)
.algolia-search-wrapper
.algolia-autocomplete
.algolia-docsearch-suggestion
.algolia-docsearch-suggestion--subcategory-column
float none
width 150px
min-width 150px
display table-cell
.algolia-docsearch-suggestion--content
float none
display table-cell
width 100%
vertical-align top
.ds-dropdown-menu
min-width 515px !important
@media (max-width: $MQMobile)
.algolia-search-wrapper
.ds-dropdown-menu
min-width calc(100vw - 4rem) !important
max-width calc(100vw - 4rem) !important
.algolia-docsearch-suggestion--wrapper
padding 5px 7px 5px 5px !important
.algolia-docsearch-suggestion--subcategory-column
padding 0 !important
background white !important
.algolia-docsearch-suggestion--subcategory-column-text:after
content " > "
font-size 10px
line-height 14.4px
display inline-block
width 5px
margin -3px 3px 0
vertical-align middle
</style>
<template>
<div
class="dropdown-wrapper"
:class="{ open }"
>
<a
class="dropdown-title"
@click="toggle"
>
<span class="title">{{ item.text }}</span>
<span
class="arrow"
:class="open ? 'down' : 'right'"
></span>
</a>
<DropdownTransition>
<ul
class="nav-dropdown"
v-show="open"
>
<li
class="dropdown-item"
:key="subItem.link || index"
v-for="(subItem, index) in item.items"
>
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
<ul
class="dropdown-subitem-wrapper"
v-if="subItem.type === 'links'"
>
<li
class="dropdown-subitem"
:key="childSubItem.link"
v-for="childSubItem in subItem.items"
>
<NavLink :item="childSubItem"/>
</li>
</ul>
<NavLink
v-else
:item="subItem"
/>
</li>
</ul>
</DropdownTransition>
</div>
</template>
<script>
import NavLink from '@theme/components/NavLink.vue'
import DropdownTransition from '@theme/components/DropdownTransition.vue'
export default {
components: { NavLink, DropdownTransition },
data () {
return {
open: false
}
},
props: {
item: {
required: true
}
},
methods: {
toggle () {
this.open = !this.open
}
}
}
</script>
<style lang="stylus">
.dropdown-wrapper
cursor pointer
.dropdown-title
display block
&:hover
border-color transparent
.arrow
vertical-align middle
margin-top -1px
margin-left 0.4rem
.nav-dropdown
.dropdown-item
color inherit
line-height 1.7rem
h4
margin 0.45rem 0 0
border-top 1px solid #eee
padding 0.45rem 1.5rem 0 1.25rem
.dropdown-subitem-wrapper
padding 0
list-style none
.dropdown-subitem
font-size 0.9em
a
display block
line-height 1.7rem
position relative
border-bottom none
font-weight 400
margin-bottom 0
padding 0 1.5rem 0 1.25rem
&:hover
color $accentColor
&.router-link-active
color $accentColor
&::after
content ""
width 0
height 0
border-left 5px solid $accentColor
border-top 3px solid transparent
border-bottom 3px solid transparent
position absolute
top calc(50% - 2px)
left 9px
&:first-child h4
margin-top 0
padding-top 0
border-top 0
@media (max-width: $MQMobile)
.dropdown-wrapper
&.open .dropdown-title
margin-bottom 0.5rem
.nav-dropdown
transition height .1s ease-out
overflow hidden
.dropdown-item
h4
border-top 0
margin-top 0
padding-top 0
h4, & > a
font-size 15px
line-height 2rem
.dropdown-subitem
font-size 14px
padding-left 1rem
@media (min-width: $MQMobile)
.dropdown-wrapper
height 1.8rem
&:hover .nav-dropdown
// override the inline style.
display block !important
.dropdown-title .arrow
// make the arrow always down at desktop
border-left 4px solid transparent
border-right 4px solid transparent
border-top 6px solid $arrowBgColor
border-bottom 0
.nav-dropdown
display none
// Avoid height shaked by clicking
height auto !important
box-sizing border-box;
max-height calc(100vh - 2.7rem)
overflow-y auto
position absolute
top 100%
right 0
background-color #fff
padding 0.6rem 0
border 1px solid #ddd
border-bottom-color #ccc
text-align left
border-radius 0.25rem
white-space nowrap
margin 0
</style>
<template>
<transition
name="dropdown"
@enter="setHeight"
@after-enter="unsetHeight"
@before-leave="setHeight"
>
<slot/>
</transition>
</template>
<script>
export default {
name: 'DropdownTransition',
methods: {
setHeight (items) {
// explicitly set height so that it can be transitioned
items.style.height = items.scrollHeight + 'px'
},
unsetHeight (items) {
items.style.height = ''
}
}
}
</script>
<style lang="stylus">
.dropdown-enter, .dropdown-leave-to
height 0 !important
</style>
<template>
<main class="home" aria-labelledby="main-title">
<div class="flex-container">
<header class="hero">
<img
v-if="data.heroImage"
src='../assets/hero.png'
:alt="data.heroAlt || 'hero'"
>
<h1 v-if="data.heroText !== null" id="main-title">{{ data.heroText || $title || 'Hello' }}</h1>
<p
class="action"
v-if="data.actionText && data.actionLink"
>
<NavLink
class="action-button"
:item="actionLink"
/>
</p>
</header>
<div id="link">
<div class="block" v-for="item in link">
<a :href= "item.url">{{item.title}}</a>
</div>
</div>
</div>
<Content class="theme-default-content custom"/>
<div
class="footer"
v-if="data.footer"
>
{{ data.footer }}
</div>
</main>
</template>
<script>
import NavLink from '@theme/components/NavLink.vue'
export default {
data : () => {
return {
link: [
{title: '教程', url: '/reference'},
{title: '框架', url: '/framework'},
{title: '组件', url: '/component'},
{title: 'API', url: '/api'}
]
}
},
components: { NavLink },
computed: {
data () {
return this.$page.frontmatter
},
actionLink () {
return {
link: this.data.actionLink,
text: this.data.actionText
}
}
}
}
</script>
<style lang="stylus">
.home
#link
text-align center
justify-content center
.block
text-align: center;
display inline-block
min-width 2.5rem
background-color #fff
border 1px solid #ebeef5
box-shadow 0 2px 12px 0 rgba(0,0,0,.1)
padding .6rem 1rem
margin .5rem 1rem
border-radius 1.5rem
height 100vh
display flex
flex-direction column
justify-content space-evenly
padding $navbarHeight 2rem 0
max-width 960px
margin 0px auto
.hero
text-align center
img
max-width: 40%
max-height 280px
display block
margin 3rem auto 1.5rem
h1
font-size 1.2rem
h1, .description, .action
margin 1.8rem auto
.description
max-width 35rem
font-size 1.6rem
line-height 1.3
color lighten($textColor, 40%)
.action-button
display inline-block
font-size 1.2rem
color #fff
background-color $accentColor
padding 0.8rem 1.6rem
border-radius 4px
transition background-color .1s ease
box-sizing border-box
border-bottom 1px solid darken($accentColor, 10%)
&:hover
background-color lighten($accentColor, 10%)
.footer
padding 2.5rem
border-top 1px solid $borderColor
text-align center
color lighten($textColor, 25%)
@media (max-width: $MQMobile)
.home
.features
flex-direction column
.feature
max-width 100%
padding 0 2.5rem
@media (max-width: $MQMobileNarrow)
.home
padding-left 1.5rem
padding-right 1.5rem
.hero
img
max-height 210px
margin 2rem auto 1.2rem
h1
font-size 1.5rem
h1, .description, .action
margin 1.2rem auto
.description
font-size 1.2rem
.action-button
font-size 1rem
padding 0.6rem 1.2rem
.feature
h2
font-size 1.25rem
</style>
<template>
<router-link
class="nav-link"
:to="link"
v-if="!isExternal(link)"
:exact="exact"
>{{ item.text }}</router-link>
<a
v-else
:href="link"
class="nav-link external"
:target="isMailto(link) || isTel(link) ? null : '_blank'"
:rel="isMailto(link) || isTel(link) ? null : 'noopener noreferrer'"
>
{{ item.text }}
<OutboundLink/>
</a>
</template>
<script>
import { isExternal, isMailto, isTel, ensureExt } from '../util'
export default {
props: {
item: {
required: true
}
},
computed: {
link () {
return ensureExt(this.item.link)
},
exact () {
if (this.$site.locales) {
return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
}
return this.link === '/'
}
},
methods: {
isExternal,
isMailto,
isTel
}
}
</script>
<template>
<nav
class="nav-links"
v-if="userLinks.length || repoLink"
>
<!-- user links -->
<div
class="nav-item"
v-for="item in userLinks"
:key="item.link"
>
<DropdownLink
v-if="item.type === 'links'"
:item="item"
/>
<NavLink
v-else
:item="item"
/>
</div>
<!-- repo link -->
<a
v-if="repoLink"
:href="repoLink"
class="repo-link"
target="_blank"
rel="noopener noreferrer"
>
{{ repoLabel }}
<OutboundLink/>
</a>
</nav>
</template>
<script>
import DropdownLink from '@theme/components/DropdownLink.vue'
import { resolveNavLinkItem } from '../util'
import NavLink from '@theme/components/NavLink.vue'
export default {
components: { NavLink, DropdownLink },
computed: {
userNav () {
return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || []
},
nav () {
const { locales } = this.$site
if (locales && Object.keys(locales).length > 1) {
const currentLink = this.$page.path
const routes = this.$router.options.routes
const themeLocales = this.$site.themeConfig.locales || {}
const languageDropdown = {
text: this.$themeLocaleConfig.selectText || 'Languages',
items: Object.keys(locales).map(path => {
const locale = locales[path]
const text = themeLocales[path] && themeLocales[path].label || locale.lang
let link
// Stay on the current page
if (locale.lang === this.$lang) {
link = currentLink
} else {
// Try to stay on the same page
link = currentLink.replace(this.$localeConfig.path, path)
// fallback to homepage
if (!routes.some(route => route.path === link)) {
link = path
}
}
return { text, link }
})
}
return [...this.userNav, languageDropdown]
}
return this.userNav
},
userLinks () {
return (this.nav || []).map(link => {
return Object.assign(resolveNavLinkItem(link), {
items: (link.items || []).map(resolveNavLinkItem)
})
})
},
repoLink () {
const { repo } = this.$site.themeConfig
if (repo) {
return /^https?:/.test(repo)
? repo
: `https://github.com/${repo}`
}
},
repoLabel () {
if (!this.repoLink) return
if (this.$site.themeConfig.repoLabel) {
return this.$site.themeConfig.repoLabel
}
const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0]
const platforms = ['GitHub', 'GitLab', 'Bitbucket']
for (let i = 0; i < platforms.length; i++) {
const platform = platforms[i]
if (new RegExp(platform, 'i').test(repoHost)) {
return platform
}
}
return 'Source'
}
}
}
</script>
<style lang="stylus">
.nav-links
display inline-block
a
line-height 1.4rem
color inherit
&:hover, &.router-link-active
color $accentColor
.nav-item
position relative
display inline-block
margin-left 1.5rem
line-height 2rem
&:first-child
margin-left 0
.repo-link
margin-left 1.5rem
@media (max-width: $MQMobile)
.nav-links
.nav-item, .repo-link
margin-left 0
@media (min-width: $MQMobile)
.nav-links a
&:hover, &.router-link-active
color $textColor
.nav-item > a:not(.external)
&:hover, &.router-link-active
margin-bottom -2px
border-bottom 2px solid lighten($accentColor, 8%)
</style>
<template>
<header class="navbar">
<SidebarButton @toggle-sidebar="$emit('toggle-sidebar')"/>
<router-link
:to="$localePath"
class="home-link"
>
<img
class="logo"
src='../assets/quick-app-logo.png'
:alt="$siteTitle"
>
<span
ref="siteName"
class="site-name"
v-if="$siteTitle"
:class="{ 'can-hide': $site.themeConfig.logo }"
>{{ $siteTitle }}</span>
</router-link>
<div
class="links"
:style="linksWrapMaxWidth ? {
'max-width': linksWrapMaxWidth + 'px'
} : {}"
>
<AlgoliaSearchBox
v-if="isAlgoliaSearch"
:options="algolia"
/>
<SearchBox v-else-if="$site.themeConfig.search !== false && $page.frontmatter.search !== false"/>
<NavLinks class="can-hide"/>
</div>
</header>
</template>
<script>
import AlgoliaSearchBox from '@AlgoliaSearchBox'
import SearchBox from '@SearchBox'
import SidebarButton from '@theme/components/SidebarButton.vue'
import NavLinks from '@theme/components/NavLinks.vue'
export default {
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
data () {
return {
linksWrapMaxWidth: null
}
},
mounted () {
const MOBILE_DESKTOP_BREAKPOINT = 719 // refer to config.styl
const NAVBAR_VERTICAL_PADDING = parseInt(css(this.$el, 'paddingLeft')) + parseInt(css(this.$el, 'paddingRight'))
const handleLinksWrapWidth = () => {
if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) {
this.linksWrapMaxWidth = null
} else {
this.linksWrapMaxWidth = this.$el.offsetWidth - NAVBAR_VERTICAL_PADDING
- (this.$refs.siteName && this.$refs.siteName.offsetWidth || 0)
}
}
handleLinksWrapWidth()
window.addEventListener('resize', handleLinksWrapWidth, false)
},
computed: {
algolia () {
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
},
isAlgoliaSearch () {
return this.algolia && this.algolia.apiKey && this.algolia.indexName
}
}
}
function css (el, property) {
// NOTE: Known bug, will return 'auto' if style value is 'auto'
const win = el.ownerDocument.defaultView
// null means not to return pseudo styles
return win.getComputedStyle(el, null)[property]
}
</script>
<style lang="stylus">
$navbar-vertical-padding = 0.7rem
$navbar-horizontal-padding = 1.5rem
.navbar
padding $navbar-vertical-padding $navbar-horizontal-padding
line-height $navbarHeight - 1.4rem
a, span, img
display inline-block
.logo
height $navbarHeight - 1.4rem
min-width $navbarHeight - 1.4rem
margin-right 0.8rem
vertical-align top
.site-name
font-size 1.3rem
font-weight 600
color $textColor
position relative
.links
padding-left 1.5rem
box-sizing border-box
background-color white
white-space nowrap
font-size 0.9rem
position absolute
right $navbar-horizontal-padding
top $navbar-vertical-padding
display flex
.search-box
flex: 0 0 auto
vertical-align top
@media (max-width: $MQMobile)
.navbar
padding-left 4rem
.can-hide
display none
.links
padding-left 1.5rem
.site-name
width calc(100vw - 9.4rem)
overflow hidden
white-space nowrap
text-overflow ellipsis
</style>
<template>
<main class="page">
<slot name="top" />
<Content class="theme-default-content" />
<PageEdit />
<PageNav v-bind="{ sidebarItems }" />
<slot name="bottom" />
</main>
</template>
<script>
import PageEdit from '@theme/components/PageEdit.vue'
import PageNav from '@theme/components/PageNav.vue'
export default {
components: { PageEdit, PageNav },
props: ['sidebarItems']
}
</script>
<style lang="stylus">
@require '../styles/wrapper.styl';
.page {
padding-bottom: 2rem;
display: block;
}
</style>
<template>
<footer class="page-edit">
<div class="edit-link" v-if="editLink">
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{ editLinkText }}</a>
<OutboundLink />
</div>
<div class="last-updated" v-if="lastUpdated">
<span class="prefix">{{ lastUpdatedText }}:</span>
<span class="time">{{ lastUpdated }}</span>
</div>
</footer>
</template>
<script>
import { endingSlashRE, outboundRE } from '../util'
export default {
name: 'PageEdit',
computed: {
lastUpdated () {
return this.$page.lastUpdated
},
lastUpdatedText () {
if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
return this.$themeLocaleConfig.lastUpdated
}
if (typeof this.$site.themeConfig.lastUpdated === 'string') {
return this.$site.themeConfig.lastUpdated
}
return 'Last Updated'
},
editLink () {
if (this.$page.frontmatter.editLink === false) {
return
}
const {
repo,
editLinks,
docsDir = '',
docsBranch = 'master',
docsRepo = repo
} = this.$site.themeConfig
if (docsRepo && editLinks && this.$page.relativePath) {
return this.createEditLink(
repo,
docsRepo,
docsDir,
docsBranch,
this.$page.relativePath
)
}
},
editLinkText () {
return (
this.$themeLocaleConfig.editLinkText
|| this.$site.themeConfig.editLinkText
|| `Edit this page`
)
}
},
methods: {
createEditLink (repo, docsRepo, docsDir, docsBranch, path) {
const bitbucket = /bitbucket.org/
if (bitbucket.test(repo)) {
const base = outboundRE.test(docsRepo) ? docsRepo : repo
return (
base.replace(endingSlashRE, '')
+ `/src`
+ `/${docsBranch}/`
+ (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
+ path
+ `?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
)
}
const base = outboundRE.test(docsRepo)
? docsRepo
: `https://github.com/${docsRepo}`
return (
base.replace(endingSlashRE, '')
+ `/edit`
+ `/${docsBranch}/`
+ (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
+ path
)
}
}
}
</script>
<style lang="stylus">
@require '../styles/wrapper.styl';
.page-edit {
@extend $wrapper;
padding-top: 1rem;
padding-bottom: 1rem;
overflow: auto;
.edit-link {
display: inline-block;
a {
color: lighten($textColor, 25%);
margin-right: 0.25rem;
}
}
.last-updated {
float: right;
font-size: 0.9em;
.prefix {
font-weight: 500;
color: lighten($textColor, 25%);
}
.time {
font-weight: 400;
color: #aaa;
}
}
}
@media (max-width: $MQMobile) {
.page-edit {
.edit-link {
margin-bottom: 0.5rem;
}
.last-updated {
font-size: 0.8em;
float: none;
text-align: left;
}
}
}
</style>
<template>
<div class="page-nav" v-if="prev || next">
<p class="inner">
<div v-if="prev" class="prev page-navigator" v-on:click="clickPrevious">
<div class="navigation-mark">
<span class="navigation-flag">Previous</span>
<router-link style="pointer-events: none" v-if="prev" class="prev router-tag" :to="prev.path">{{ prev.title || prev.path }}</router-link>
</div>
</div>
<div v-if="next" class="next page-navigator" v-on:click="clickNext">
<div class="navigation-mark">
<span class="navigation-flag">Next</span>
<router-link style="pointer-events: none" v-if="next" class="next router-tag" :to="next.path">{{ next.title || next.path }}</router-link>
</div>
</div>
</p>
</div>
</template>
<script>
import { resolvePage } from '../util'
import isString from 'lodash/isString'
import isNil from 'lodash/isNil'
export default {
name: 'PageNav',
props: ['sidebarItems'],
computed: {
prev () {
return resolvePageLink(LINK_TYPES.PREV, this)
},
next () {
return resolvePageLink(LINK_TYPES.NEXT, this)
}
},
methods: {
clickPrevious: function () {
document.querySelector('.prev.router-tag').click()
},
clickNext: function () {
document.querySelector('.next.router-tag').click()
}
}
}
function resolvePrev (page, items) {
return find(page, items, -1)
}
function resolveNext (page, items) {
return find(page, items, 1)
}
const LINK_TYPES = {
NEXT: {
resolveLink: resolveNext,
getThemeLinkConfig: ({ nextLinks }) => nextLinks,
getPageLinkConfig: ({ frontmatter }) => frontmatter.next
},
PREV: {
resolveLink: resolvePrev,
getThemeLinkConfig: ({ prevLinks }) => prevLinks,
getPageLinkConfig: ({ frontmatter }) => frontmatter.prev
}
}
function resolvePageLink (
linkType,
{ $themeConfig, $page, $route, $site, sidebarItems }
) {
const { resolveLink, getThemeLinkConfig, getPageLinkConfig } = linkType
// Get link config from theme
const themeLinkConfig = getThemeLinkConfig($themeConfig)
// Get link config from current page
const pageLinkConfig = getPageLinkConfig($page)
// Page link config will overwrite global theme link config if defined
const link = isNil(pageLinkConfig) ? themeLinkConfig : pageLinkConfig
if (link === false) {
return
} else if (isString(link)) {
return resolvePage($site.pages, link, $route.path)
} else {
return resolveLink($page, sidebarItems)
}
}
function find (page, items, offset) {
const res = []
flatten(items, res)
for (let i = 0; i < res.length; i++) {
const cur = res[i]
if (cur.type === 'page' && cur.path === decodeURIComponent(page.path)) {
return res[i + offset]
}
}
}
function flatten (items, res) {
for (let i = 0, l = items.length; i < l; i++) {
if (items[i].type === 'group') {
flatten(items[i].children || [], res)
} else {
res.push(items[i])
}
}
}
</script>
<style lang="stylus">
@require '../styles/wrapper.styl';
.navigation-flag {
font-size: 12px;
font-family: Content-font, Roboto, sans-serif;
font-weight: 400;
line-height: 1.625;
color: #9DAAB6;
}
.navigation-mark {
display: flex;
flex-direction: column;
}
.page-navigator {
box-shadow: rgba(116, 129, 141, 0.1) 0px 3px 8px 0px;
border-width: 1px;
border-style: solid;
border-color: rgb(230, 236, 241);
width: 40%;
height: 50px;
display: inline-flex;
padding: 0 10px;
flex-direction: row;
align-items: center;
justify-content: space-around;
cursor: pointer;
}
.page-navigator:hover {
border-color: #4761f6;
color: #4761f6;
}
.page-nav {
@extend $wrapper;
padding-top: 1rem;
padding-bottom: 0;
.inner {
min-height: 2rem;
margin-top: 0;
border-top: 1px solid $borderColor;
padding-top: 1rem;
overflow: auto; // clear float
}
.router-tag {
font-size: 16px;
font-family: Content-font, Roboto, sans-serif;
font-weight: 700;
line-height: 1.5;
}
.next {
float: right;
}
}
</style>
<template>
<aside class="sidebar">
<NavLinks/>
<slot name="top"/>
<SidebarLinks :depth="0" :items="items"/>
<slot name="bottom"/>
</aside>
</template>
<script>
import SidebarLinks from '@theme/components/SidebarLinks.vue'
import NavLinks from '@theme/components/NavLinks.vue'
export default {
name: 'Sidebar',
components: { SidebarLinks, NavLinks },
props: ['items']
}
</script>
<style lang="stylus">
.sidebar
ul
padding 0
margin 0
list-style-type none
a
display inline-block
.nav-links
display none
border-bottom 1px solid $borderColor
padding 0.5rem 0 0.75rem 0
a
font-weight 600
.nav-item, .repo-link
display block
line-height 1.25rem
font-size 1.1em
padding 0.5rem 0 0.5rem 1.5rem
& > .sidebar-links
padding 1.5rem 0
& > li > a.sidebar-link
font-size 1.1em
line-height 1.7
font-weight bold
& > li:not(:first-child)
margin-top .75rem
@media (max-width: $MQMobile)
.sidebar
.nav-links
display block
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
top calc(1rem - 2px)
& > .sidebar-links
padding 1rem 0
</style>
<template>
<div class="sidebar-button" @click="$emit('toggle-sidebar')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512">
<path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class=""></path>
</svg>
</div>
</template>
<style lang="stylus">
.sidebar-button
cursor pointer
display none
width 1.25rem
height 1.25rem
position absolute
padding 0.6rem
top 0.6rem
left 1rem
.icon
display block
width 1.25rem
height 1.25rem
@media (max-width: $MQMobile)
.sidebar-button
display block
</style>
<template>
<section
class="sidebar-group"
:class="[
{
collapsable,
'is-sub-group': depth !== 0
},
`depth-${depth}`
]"
>
<router-link
v-if="item.path"
class="sidebar-heading clickable"
:class="{
open,
'active': isActive($route, item.path)
}"
:to="item.path"
@click.native="$emit('toggle')"
>
<span>{{ item.title }}</span>
<span
class="arrow"
v-if="collapsable"
:class="open ? 'down' : 'right'">
</span>
</router-link>
<p
v-else
class="sidebar-heading"
:class="{ open }"
@click="$emit('toggle')"
>
<span>{{ item.title }}</span>
<span
class="arrow"
v-if="collapsable"
:class="open ? 'down' : 'right'">
</span>
</p>
<DropdownTransition>
<SidebarLinks
class="sidebar-group-items"
:items="item.children"
v-if="open || !collapsable"
:sidebarDepth="item.sidebarDepth"
:depth="depth + 1"
/>
</DropdownTransition>
</section>
</template>
<script>
import { isActive } from '../util'
import DropdownTransition from '@theme/components/DropdownTransition.vue'
export default {
name: 'SidebarGroup',
props: ['item', 'open', 'collapsable', 'depth'],
components: { DropdownTransition },
// ref: https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
beforeCreate () {
this.$options.components.SidebarLinks = require('./SidebarLinks.vue').default
},
methods: { isActive }
}
</script>
<style lang="stylus">
.sidebar-group
.sidebar-group
padding-left 0.5em
&:not(.collapsable)
.sidebar-heading:not(.clickable)
cursor auto
color inherit
// refine styles of nested sidebar groups
&.is-sub-group
padding-left 0
& > .sidebar-heading
font-size 0.95em
line-height 1.4
font-weight normal
padding-left 2rem
&:not(.clickable)
opacity 0.5
& > .sidebar-group-items
padding-left 1rem
& > li > .sidebar-link
font-size: 0.95em;
border-left none
&.depth-2
& > .sidebar-heading
border-left none
.sidebar-heading
color $textColor
transition color .15s ease
cursor pointer
font-size 1.1em
font-weight bold
// text-transform uppercase
padding 0.35rem 1.5rem 0.35rem 1.25rem
width 100%
box-sizing border-box
margin 0
border-left 0.25rem solid transparent
&.open, &:hover
color inherit
.arrow
position relative
top -0.12em
left 0.5em
&.clickable
&.active
font-weight 600
color $accentColor
border-left-color $accentColor
&:hover
color $accentColor
.sidebar-group-items
transition height .1s ease-out
font-size 0.95em
overflow hidden
</style>
<script>
import { isActive, hashRE, groupHeaders } from '../util'
export default {
functional: true,
props: ['item', 'sidebarDepth'],
render (h,
{
parent: {
$page,
$site,
$route,
$themeConfig,
$themeLocaleConfig
},
props: {
item,
sidebarDepth
}
}) {
// use custom active class matching logic
// due to edge case of paths ending with / + hash
const selfActive = isActive($route, item.path)
// for sidebar: auto pages, a hash link should be active if one of its child
// matches
const active = item.type === 'auto'
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
: selfActive
const link = item.type === 'external'
? renderExternal(h, item.path, item.title || item.path)
: renderLink(h, item.path, item.title || item.path, active)
const maxDepth = [
$page.frontmatter.sidebarDepth,
sidebarDepth,
$themeLocaleConfig.sidebarDepth,
$themeConfig.sidebarDepth,
1
].find(depth => depth !== undefined)
const displayAllHeaders = $themeLocaleConfig.displayAllHeaders
|| $themeConfig.displayAllHeaders
if (item.type === 'auto') {
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
} else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
const children = groupHeaders(item.headers)
return [link, renderChildren(h, children, item.path, $route, maxDepth)]
} else {
return link
}
}
}
function renderLink (h, to, text, active) {
return h('router-link', {
props: {
to,
activeClass: '',
exactActiveClass: ''
},
class: {
active,
'sidebar-link': true
}
}, text)
}
function renderChildren (h, children, path, route, maxDepth, depth = 1) {
if (!children || depth > maxDepth) return null
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
const active = isActive(route, path + '#' + c.slug)
return h('li', { class: 'sidebar-sub-header' }, [
renderLink(h, path + '#' + c.slug, c.title, active),
renderChildren(h, c.children, path, route, maxDepth, depth + 1)
])
}))
}
function renderExternal (h, to, text) {
return h('a', {
attrs: {
href: to,
target: '_blank',
rel: 'noopener noreferrer'
},
class: {
'sidebar-link': true
}
}, [text, h('OutboundLink')])
}
</script>
<style lang="stylus">
.sidebar .sidebar-sub-headers
padding-left 1rem
font-size 0.95em
a.sidebar-link
font-size 1em
font-weight 400
display inline-block
color $textColor
border-left 0.25rem solid transparent
padding 0.35rem 1rem 0.35rem 1.25rem
line-height 1.4
width: 100%
box-sizing: border-box
&:hover
color $accentColor
&.active
font-weight 600
color $accentColor
border-left-color $accentColor
.sidebar-group &
padding-left 2rem
.sidebar-sub-headers &
padding-top 0.25rem
padding-bottom 0.25rem
border-left none
&.active
font-weight 500
</style>
<template>
<ul
class="sidebar-links"
v-if="items.length"
>
<li v-for="(item, i) in items" :key="i">
<SidebarGroup
v-if="item.type === 'group'"
:item="item"
:open="openGroupIndex.indexOf(i) > -1"
:collapsable="item.collapsable || item.collapsible"
:depth="depth"
@toggle="toggleGroup(i)"
/>
<SidebarLink
v-else
:sidebarDepth="sidebarDepth"
:item="item"
/>
</li>
</ul>
</template>
<script>
import SidebarGroup from '@theme/components/SidebarGroup.vue'
import SidebarLink from '@theme/components/SidebarLink.vue'
import { isActive } from '../util'
export default {
name: 'SidebarLinks',
components: { SidebarGroup, SidebarLink },
props: [
'items',
'depth', // depth of current sidebar links
'sidebarDepth' // depth of headers to be extracted
],
data () {
// 默认打开每一栏的第一项
return {
openGroupIndex: [0]
}
},
created () {
this.refreshIndex()
},
watch: {
'$route' () {
this.refreshIndex()
}
},
methods: {
refreshIndex () {
const index = resolveOpenGroupIndex(
this.$route,
this.items
)
if (index > -1) {
this.openGroupIndex.push(index)
}
},
toggleGroup (index) {
const ifExist = this.openGroupIndex.indexOf(index)
if(ifExist > -1) {
this.openGroupIndex.splice(ifExist, 1)
} else {
this.openGroupIndex.push(index)
}
},
isActive (page) {
return isActive(this.$route, page.regularPath)
}
}
}
function resolveOpenGroupIndex (route, items) {
for (let i = 0; i < items.length; i++) {
const item = items[i]
if (descendantIsActive(route, item)) {
return i
}
}
return -1
}
function descendantIsActive (route, item) {
if (item.type === 'group') {
return item.children.some(child => {
if (child.type === 'group') {
return descendantIsActive(route, child)
} else {
return child.type === 'page' && isActive(route, child.path)
}
})
}
return false
}
</script>
<script>
export default {
functional: true,
props: {
type: {
type: String,
default: 'tip'
},
text: String,
vertical: {
type: String,
default: 'top'
}
},
render (h, { props, slots }) {
return h('span', {
class: ['badge', props.type],
style: {
verticalAlign: props.vertical
}
}, props.text || slots().default)
}
}
</script>
<style lang="stylus" scoped>
.badge
display inline-block
font-size 14px
height 18px
line-height 18px
border-radius 3px
padding 0 6px
color white
background-color #42b983
&.tip, &.green
background-color #42b983
&.error
background-color #DA5961 //#f66
&.warning, &.warn, &.yellow
background-color darken(#ffe564, 35%)
& + &
margin-left 5px
</style>
const path = require('path')
// Theme API.
module.exports = (options, ctx) => ({
alias() {
const { themeConfig, siteConfig } = ctx
// resolve algolia
const isAlgoliaSearch =
themeConfig.algolia ||
Object.keys((siteConfig.locales && themeConfig.locales) || {}).some(base => themeConfig.locales[base].algolia)
return {
'@AlgoliaSearchBox': isAlgoliaSearch
? path.resolve(__dirname, 'components/AlgoliaSearchBox.vue')
: path.resolve(__dirname, 'noopModule.js')
}
},
plugins: [
['@vuepress/active-header-links', options.activeHeaderLinks],
'@vuepress/search',
'@vuepress/plugin-nprogress',
[
'container',
{
type: 'tip',
defaultTitle: {
'/zh/': '提示'
}
}
],
[
'container',
{
type: 'warning',
defaultTitle: {
'/zh/': '注意'
}
}
],
[
'container',
{
type: 'danger',
defaultTitle: {
'/zh/': '警告'
}
}
]
]
})
<template>
<div class="theme-container">
<div class="theme-default-content">
<h1>404</h1>
<blockquote>{{ getMsg() }}</blockquote>
<router-link to="/">Take me home.</router-link>
</div>
</div>
</template>
<script>
const msgs = [
`There's nothing here.`,
`How did we get here?`,
`That's a Four-Oh-Four.`,
`Looks like we've got some broken links.`
]
export default {
methods: {
getMsg () {
return msgs[Math.floor(Math.random() * msgs.length)]
}
}
}
</script>
<template>
<div
class="theme-container"
:class="pageClasses"
@touchstart="onTouchStart"
@touchend="onTouchEnd"
>
<Navbar
v-if="shouldShowNavbar"
@toggle-sidebar="toggleSidebar"
/>
<div
class="sidebar-mask"
@click="toggleSidebar(false)"
></div>
<Sidebar
:items="sidebarItems"
@toggle-sidebar="toggleSidebar"
>
<slot
name="sidebar-top"
slot="top"
/>
<slot
name="sidebar-bottom"
slot="bottom"
/>
</Sidebar>
<Home v-if="$page.frontmatter.home"/>
<Page
v-else
:sidebar-items="sidebarItems"
>
<slot
name="page-top"
slot="top"
/>
<slot
name="page-bottom"
slot="bottom"
/>
</Page>
</div>
</template>
<script>
import Home from '@theme/components/Home.vue'
import Navbar from '@theme/components/Navbar.vue'
import Page from '@theme/components/Page.vue'
import Sidebar from '@theme/components/Sidebar.vue'
import { resolveSidebarItems } from '../util'
export default {
components: { Home, Page, Sidebar, Navbar },
data () {
return {
isSidebarOpen: false
}
},
computed: {
shouldShowNavbar () {
const { themeConfig } = this.$site
const { frontmatter } = this.$page
if (
frontmatter.navbar === false
|| themeConfig.navbar === false) {
return false
}
return (
this.$title
|| themeConfig.logo
|| themeConfig.repo
|| themeConfig.nav
|| this.$themeLocaleConfig.nav
)
},
shouldShowSidebar () {
const { frontmatter } = this.$page
return (
!frontmatter.home
&& frontmatter.sidebar !== false
&& this.sidebarItems.length
)
},
sidebarItems () {
return resolveSidebarItems(
this.$page,
this.$page.regularPath,
this.$site,
this.$localePath
)
},
pageClasses () {
const userPageClass = this.$page.frontmatter.pageClass
return [
{
'no-navbar': !this.shouldShowNavbar,
'sidebar-open': this.isSidebarOpen,
'no-sidebar': !this.shouldShowSidebar
},
userPageClass
]
}
},
mounted () {
this.$router.afterEach(() => {
this.isSidebarOpen = false
})
},
methods: {
toggleSidebar (to) {
this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
this.$emit('toggle-sidebar', this.isSidebarOpen)
},
// side swipe
onTouchStart (e) {
this.touchStart = {
x: e.changedTouches[0].clientX,
y: e.changedTouches[0].clientY
}
},
onTouchEnd (e) {
const dx = e.changedTouches[0].clientX - this.touchStart.x
const dy = e.changedTouches[0].clientY - this.touchStart.y
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
if (dx > 0 && this.touchStart.x <= 80) {
this.toggleSidebar(true)
} else {
this.toggleSidebar(false)
}
}
}
}
}
</script>
@require './config'
.arrow
display inline-block
width 0
height 0
&.up
border-left 4px solid transparent
border-right 4px solid transparent
border-bottom 6px solid $arrowBgColor
&.down
border-left 4px solid transparent
border-right 4px solid transparent
border-top 6px solid $arrowBgColor
&.right
border-top 4px solid transparent
border-bottom 4px solid transparent
border-left 6px solid $arrowBgColor
&.left
border-top 4px solid transparent
border-bottom 4px solid transparent
border-right 6px solid $arrowBgColor
{$contentClass}
code
color lighten($textColor, 20%)
padding 0.25rem 0.5rem
margin 0
font-size 0.85em
background-color rgba(27,31,35,0.05)
border-radius 3px
.token
&.deleted
color #EC5975
&.inserted
color $accentColor
{$contentClass}
pre, pre[class*="language-"]
line-height 1.4
padding 1.25rem 1.5rem
margin 0.85rem 0
background-color $codeBgColor
border-radius 6px
overflow auto
code
color #fff
padding 0
background-color transparent
border-radius 0
div[class*="language-"]
position relative
background-color $codeBgColor
border-radius 6px
.highlight-lines
user-select none
padding-top 1.3rem
position absolute
top 0
left 0
width 100%
line-height 1.4
.highlighted
background-color rgba(0, 0, 0, 66%)
pre, pre[class*="language-"]
background transparent
position relative
z-index 1
&::before
position absolute
z-index 3
top 0.8em
right 1em
font-size 0.75rem
color rgba(255, 255, 255, 0.4)
&:not(.line-numbers-mode)
.line-numbers-wrapper
display none
&.line-numbers-mode
.highlight-lines .highlighted
position relative
&:before
content ' '
position absolute
z-index 3
left 0
top 0
display block
width $lineNumbersWrapperWidth
height 100%
background-color rgba(0, 0, 0, 66%)
pre
padding-left $lineNumbersWrapperWidth + 1 rem
vertical-align middle
.line-numbers-wrapper
position absolute
top 0
width $lineNumbersWrapperWidth
text-align center
color rgba(255, 255, 255, 0.3)
padding 1.25rem 0
line-height 1.4
br
user-select none
.line-number
position relative
z-index 4
user-select none
font-size 0.85em
&::after
content ''
position absolute
z-index 2
top 0
left 0
width $lineNumbersWrapperWidth
height 100%
border-radius 6px 0 0 6px
border-right 1px solid rgba(0, 0, 0, 66%)
background-color $codeBgColor
for lang in $codeLang
div{'[class~="language-' + lang + '"]'}
&:before
content ('' + lang)
div[class~="language-javascript"]
&:before
content "js"
div[class~="language-typescript"]
&:before
content "ts"
div[class~="language-markup"]
&:before
content "html"
div[class~="language-markdown"]
&:before
content "md"
div[class~="language-json"]:before
content "json"
div[class~="language-ruby"]:before
content "rb"
div[class~="language-python"]:before
content "py"
div[class~="language-bash"]:before
content "sh"
div[class~="language-php"]:before
content "php"
@import '~prismjs/themes/prism-tomorrow.css'
$contentClass = '.theme-default-content'
.custom-block
.custom-block-title
font-weight 600
margin-bottom -0.4rem
&.tip, &.warning, &.danger
padding .1rem 1.5rem
border-left-width .5rem
border-left-style solid
margin 1rem 0
&.tip
background-color #f3f5f7
border-color #42b983
&.warning
background-color rgba(255,229,100,.3)
border-color darken(#ffe564, 35%)
color darken(#ffe564, 70%)
.custom-block-title
color darken(#ffe564, 50%)
a
color $textColor
&.danger
background-color #ffe6e6
border-color darken(red, 20%)
color darken(red, 70%)
.custom-block-title
color darken(red, 40%)
a
color $textColor
@require './config'
@require './code'
@require './custom-blocks'
@require './arrow'
@require './wrapper'
@require './toc'
html, body
padding 0
margin 0
background-color #fff
body
font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
font-size 16px
color $textColor
.page
padding-left $sidebarWidth
.navbar
position fixed
z-index 20
top 0
left 0
right 0
height $navbarHeight
background-color #fff
box-sizing border-box
border-bottom 1px solid $borderColor
.sidebar-mask
position fixed
z-index 9
top 0
left 0
width 100vw
height 100vh
display none
.sidebar
font-size 16px
background-color #fff
width $sidebarWidth
position fixed
z-index 10
margin 0
top $navbarHeight
left 0
bottom 0
box-sizing border-box
border-right 1px solid $borderColor
overflow-y auto
{$contentClass}:not(.custom)
@extend $wrapper
> *:first-child
margin-top $navbarHeight
a:hover
text-decoration underline
p.demo
padding 1rem 1.5rem
border 1px solid #ddd
border-radius 4px
img
max-width 100%
{$contentClass}.custom
padding 0
margin 0
img
max-width 100%
a
font-weight 500
color $accentColor
text-decoration none
p a code
font-weight 400
color $accentColor
kbd
background #eee
border solid 0.15rem #ddd
border-bottom solid 0.25rem #ddd
border-radius 0.15rem
padding 0 0.15em
blockquote
font-size 1rem
color #999;
border-left .2rem solid #dfe2e5
margin 1rem 0
padding .25rem 0 .25rem 1rem
& > p
margin 0
ul, ol
padding-left 1.2em
strong
font-weight 600
h1, h2, h3, h4, h5, h6
font-weight 600
line-height 1.25
{$contentClass}:not(.custom) > &
margin-top (0.5rem - $navbarHeight)
padding-top ($navbarHeight + 1rem)
margin-bottom 0
&:first-child
margin-top -1.5rem
margin-bottom 1rem
+ p, + pre, + .custom-block
margin-top 2rem
&:hover .header-anchor
opacity: 1
h1
font-size 2.2rem
h2
font-size 1.65rem
padding-bottom .3rem
border-bottom 1px solid $borderColor
h3
font-size 1.35rem
a.header-anchor
font-size 0.85em
float left
margin-left -0.87em
padding-right 0.23em
margin-top 0.125em
opacity 0
&:hover
text-decoration none
code, kbd, .line-number
font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace
p, ul, ol
line-height 1.7
hr
border 0
border-top 1px solid $borderColor
table
border-collapse collapse
margin 1rem 0
display: block
overflow-x: auto
tr
border-top 1px solid #dfe2e5
&:nth-child(2n)
background-color #f6f8fa
th, td
border 1px solid #dfe2e5
padding .6em 1em
.theme-container
&.sidebar-open
.sidebar-mask
display: block
&.no-navbar
{$contentClass}:not(.custom) > h1, h2, h3, h4, h5, h6
margin-top 1.5rem
padding-top 0
.sidebar
top 0
@media (min-width: ($MQMobile + 1px))
.theme-container.no-sidebar
.sidebar
display none
.page
padding-left 0
@require 'mobile.styl'
@require './config'
$mobileSidebarWidth = $sidebarWidth * 0.82
// narrow desktop / iPad
@media (max-width: $MQNarrow)
.sidebar
font-size 15px
width $mobileSidebarWidth
.page
padding-left $mobileSidebarWidth
// wide mobile
@media (max-width: $MQMobile)
.sidebar
top 0
padding-top $navbarHeight
transform translateX(-100%)
transition transform .2s ease
.page
padding-left 0
.theme-container
&.sidebar-open
.sidebar
transform translateX(0)
&.no-navbar
.sidebar
padding-top: 0
// narrow mobile
@media (max-width: $MQMobileNarrow)
h1
font-size 1.9rem
{$contentClass}
div[class*="language-"]
margin 0.85rem -1.5rem
border-radius 0
.table-of-contents
.badge
vertical-align middle
$wrapper
max-width $contentWidth
margin 0 auto
padding 2rem 2.5rem
@media (max-width: $MQNarrow)
padding 2rem
@media (max-width: $MQMobileNarrow)
padding 1.5rem
export const hashRE = /#.*$/
export const extRE = /\.(md|html)$/
export const endingSlashRE = /\/$/
export const outboundRE = /^[a-z]+:/i
export function normalize(path) {
return decodeURI(path)
.replace(hashRE, '')
.replace(extRE, '')
}
export function getHash(path) {
const match = path.match(hashRE)
if (match) {
return match[0]
}
}
export function isExternal(path) {
return outboundRE.test(path)
}
export function isMailto(path) {
return /^mailto:/.test(path)
}
export function isTel(path) {
return /^tel:/.test(path)
}
export function ensureExt(path) {
if (isExternal(path)) {
return path
}
const hashMatch = path.match(hashRE)
const hash = hashMatch ? hashMatch[0] : ''
const normalized = normalize(path)
if (endingSlashRE.test(normalized)) {
return path
}
return normalized + '.html' + hash
}
export function isActive(route, path) {
const routeHash = route.hash
const linkHash = getHash(path)
if (linkHash && routeHash !== linkHash) {
return false
}
const routePath = normalize(route.path)
const pagePath = normalize(path)
return routePath === pagePath
}
export function resolvePage(pages, rawPath, base) {
if (isExternal(rawPath)) {
return {
type: 'external',
path: rawPath
}
}
if (base) {
rawPath = resolvePath(rawPath, base)
}
const path = normalize(rawPath)
for (let i = 0; i < pages.length; i++) {
if (normalize(pages[i].regularPath) === path) {
return Object.assign({}, pages[i], {
type: 'page',
path: ensureExt(pages[i].path)
})
}
}
console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`)
return {}
}
function resolvePath(relative, base, append) {
const firstChar = relative.charAt(0)
if (firstChar === '/') {
return relative
}
if (firstChar === '?' || firstChar === '#') {
return base + relative
}
const stack = base.split('/')
// remove trailing segment if:
// - not appending
// - appending to trailing slash (last segment is empty)
if (!append || !stack[stack.length - 1]) {
stack.pop()
}
// resolve relative path
const segments = relative.replace(/^\//, '').split('/')
for (let i = 0; i < segments.length; i++) {
const segment = segments[i]
if (segment === '..') {
stack.pop()
} else if (segment !== '.') {
stack.push(segment)
}
}
// ensure leading slash
if (stack[0] !== '') {
stack.unshift('')
}
return stack.join('/')
}
/**
* @param { Page } page
* @param { string } regularPath
* @param { SiteData } site
* @param { string } localePath
* @returns { SidebarGroup }
*/
export function resolveSidebarItems(page, regularPath, site, localePath) {
const { pages, themeConfig } = site
const localeConfig = localePath && themeConfig.locales ? themeConfig.locales[localePath] || themeConfig : themeConfig
const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar
if (pageSidebarConfig === 'auto') {
return resolveHeaders(page)
}
const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar
if (!sidebarConfig) {
return []
} else {
const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig)
return config ? config.map(item => resolveItem(item, pages, base)) : []
}
}
/**
* @param { Page } page
* @returns { SidebarGroup }
*/
function resolveHeaders(page) {
const headers = groupHeaders(page.headers || [])
return [
{
type: 'group',
collapsable: false,
title: page.title,
path: null,
children: headers.map(h => ({
type: 'auto',
title: h.title,
basePath: page.path,
path: page.path + '#' + h.slug,
children: h.children || []
}))
}
]
}
export function groupHeaders(headers) {
// group h3s under h2
headers = headers.map(h => Object.assign({}, h))
let lastH2
headers.forEach(h => {
if (h.level === 2) {
lastH2 = h
} else if (lastH2) {
;(lastH2.children || (lastH2.children = [])).push(h)
}
})
return headers.filter(h => h.level === 2)
}
export function resolveNavLinkItem(linkItem) {
return Object.assign(linkItem, {
type: linkItem.items && linkItem.items.length ? 'links' : 'link'
})
}
/**
* @param { Route } route
* @param { Array<string|string[]> | Array<SidebarGroup> | [link: string]: SidebarConfig } config
* @returns { base: string, config: SidebarConfig }
*/
export function resolveMatchingConfig(regularPath, config) {
if (Array.isArray(config)) {
return {
base: '/',
config: config
}
}
for (const base in config) {
if (ensureEndingSlash(regularPath).indexOf(encodeURI(base)) === 0) {
return {
base,
config: config[base]
}
}
}
return {}
}
function ensureEndingSlash(path) {
return /(\.html|\/)$/.test(path) ? path : path + '/'
}
function resolveItem(item, pages, base, groupDepth = 1) {
if (typeof item === 'string') {
return resolvePage(pages, item, base)
} else if (Array.isArray(item)) {
return Object.assign(resolvePage(pages, item[0], base), {
title: item[1]
})
} else {
if (groupDepth > 10) {
console.error('[vuepress] detected a too deep nested sidebar group.')
}
const children = item.children || []
if (children.length === 0 && item.path) {
return Object.assign(resolvePage(pages, item.path, base), {
title: item.title
})
}
return {
type: 'group',
path: item.path,
title: item.title,
sidebarDepth: item.sidebarDepth,
children: children.map(child => resolveItem(child, pages, base, groupDepth + 1)),
collapsable: item.collapsable !== false
}
}
}
---
home: true
heroText: 欢迎来到快应用 Light版
heroImage: ./assets/hero.png
tagline: 快应用文档(Light 版)
actionText: Getting Started →
actionLink: /framework/
features:
- title: 什么是快应用?
details: 快应用是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。
- title: 奇美拉是什么?
details: 兼顾微信小程序的语法开发出的快应用平台,使用户可以用微信小程序低成本的接入快应用平台。
- title: 通过文档你将学会
details: 快应用奇美拉版本(小程序兼容版本)的使用方法。
footer: MIT Licensed | Copyright © 2019-present quickapp team
---
## 基础
| 名称 | 功能说明 |
| --- | --- |
| [qa.canIUse](/api/base/qa.canIUse.html) | 判断指定API(含调用方式/回调/参数/返回值/返回值的属性)或者组件(含属性)在当前快应用版本是否可用。 |
| [qa.base64ToArrayBuffer](/api/base/qa.base64ToArrayBuffer.html) | 将 Base64 字符串转成 ArrayBuffer 对象 |
| [qa.arrayBufferToBase64](/api/base/qa.arrayBufferToBase64.html) | 将 ArrayBuffer 对象转成 Base64 字符串 |
### 系统
#### 系统信息
| 名称 | 功能说明 |
| --- | --- |
| [qa.getSystemInfoSync](/api/base/system/qa.getSystemInfoSync.html) | [qa.getSystemInfo](/api/base/system/qa.getSystemInfo.html) 的同步版本接口 |
| [qa.getSystemInfo](/api/base/system/qa.getSystemInfo.html) | 获取系统信息 |
### 快应用
#### 应用级事件
| 名称 | 功能说明 |
| ----------------------------------------------------------- | ------------------------------------ |
| [qa.onError](/api/base/app/qa.onError.html) | 监听快应用错误事件 |
| [qa.onAppShow](/api/base/app/qa.onAppShow.html) | 监听快应用切前台事件 |
| [qa.onAppHide](/api/base/app/qa.onAppHide.html) | 监听快应用切后台事件 |
| [qa.offPageNotFound](/api/base/app/qa.offPageNotFound.html) | 取消监听快应用要打开的页面不存在事件 |
| [qa.offError](/api/base/app/qa.offError.html) | 取消监听快应用错误事件 |
| [qa.offAppShow](/api/base/app/qa.offAppShow.html) | 取消监听快应用切前台事件 |
| [qa.offAppHide](/api/base/app/qa.offAppHide.html) | 取消监听快应用切后台事件 |
### 调试
| 名称 | 功能说明 |
| ----------------------------------------------------------- | -------------------- |
| [qa.setEnableDebug](/api/base/debug/qa.setEnableDebug.html) | 设置是否打开调试开关 |
#### console
| 名称 | 功能说明 |
| --- | --- |
| [console.debug](/api/base/debug/console.debug.html) | 向调试面板中打印 debug 日志 |
| [console.error](/api/base/debug/console.error.html) | 向调试面板中打印 error 日志 |
| [console.group](/api/base/debug/console.group.html) | 在调试面板中创建一个新的分组 |
| [console.groupEnd](/api/base/debug/console.groupEnd.html) | 结束由 [console.group](/api/base/debug/console.group.html) 创建的分组 |
| [console.info](/api/base/debug/console.info.html) | 向调试面板中打印 info 日志 |
| [console.log](/api/base/debug/console.log.html) | 向调试面板中打印 log 日志 |
| [console.warn](/api/base/debug/console.warn.html) | 向调试面板中打印 warn 日志 |
### 定时器
| 名称 | 功能说明 |
| --------------------------------------------------- | ------------------------------- |
| [clearInterval](/api/base/timer/clearInterval.html) | 取消由 setInterval 设置的定时器 |
| [clearTimeout](/api/base/timer/clearTimeout.html) | 取消由 setTimeout 设置的定时器 |
| [setInterval](/api/base/timer/setInterval.html) | 设定一个定时器 |
| [setTimeout](/api/base/timer/setTimeout.html) | 设定一个定时器 |
## 路由
| 名称 | 功能说明 |
| -------------------------------------------------- | ------------------------------------------------ |
| [qa.switchTab](/api/route/qa.switchTab.html) | 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 |
| [qa.reLaunch](/api/route/qa.reLaunch.html) | 关闭所有页面,打开到应用内的某个页面 |
| [qa.redirectTo](/api/route/qa.redirectTo.html) | 关闭当前页面,跳转到应用内的某个页面 |
| [qa.navigateTo](/api/route/qa.navigateTo.html) | 保留当前页面,跳转到应用内的某个页面 |
| [qa.navigateBack](/api/route/qa.navigateBack.html) | 关闭当前页面,返回上一页面或多级页面 |
## 界面
### 交互
| 名称 | 功能说明 |
| ----------------------------------------------------------------- | ------------------- |
| [qa.showToast](/api/ui/interaction/qa.showToast.html) | 显示消息提示框 |
| [qa.showModal](/api/ui/interaction/qa.showModal.html) | 显示模态对话框 |
| [qa.showLoading](/api/ui/interaction/qa.showLoading.html) | 显示 loading 提示框 |
| [qa.showActionSheet](/api/ui/interaction/qa.showActionSheet.html) | 显示操作菜单 |
| [qa.hideToast](/api/ui/interaction/qa.hideToast.html) | 隐藏消息提示框 |
| [qa.hideLoading](/api/ui/interaction/qa.hideLoading.html) | 隐藏 loading 提示框 |
### 导航栏
| 名称 | 功能说明 |
| --- | --- |
| [qa.showNavigationBarLoading](/api/ui/navigation-bar/qa.showNavigationBarLoading.html) | 显示当前页面的导航条加载动画 |
| [qa.setNavigationBarTitle](/api/ui/navigation-bar/qa.setNavigationBarTitle.html) | 设置当前当前页面的标题,即时生效 |
| [qa.setNavigationBarColor](/api/ui/navigation-bar/qa.setNavigationBarColor.html) | 设置当前页面的导航条颜色 |
| [qa.hideNavigationBarLoading](/api/ui/navigation-bar/qa.hideNavigationBarLoading.html) | 隐藏当前页面的导航条加载动画 |
### 背景
| 名称 | 功能说明 |
| --- | --- |
| [qa.setBackgroundTextStyle](/api/ui/background/qa.setBackgroundTextStyle.html) | 动态设置下拉背景字体、loading 图的样式 |
| [qa.setBackgroundColor](/api/ui/background/qa.setBackgroundColor.html) | 动态设置窗口的背景色 |
### Tab Bar
| 名称 | 功能说明 |
| ----------------------------------------------------------------- | -------------------------------- |
| [qa.showTabBarRedDot](/api/ui/tab-bar/qa.showTabBarRedDot.html) | 显示 tabBar 某一项的右上角的红点 |
| [qa.showTabBar](/api/ui/tab-bar/qa.showTabBar.html) | 显示 tabBar |
| [qa.setTabBarStyle](/api/ui/tab-bar/qa.setTabBarStyle.html) | 动态设置 tabBar 的整体样式 |
| [qa.setTabBarItem](/api/ui/tab-bar/qa.setTabBarItem.html) | 动态设置 tabBar 某一项的内容 |
| [qa.setTabBarBadge](/api/ui/tab-bar/qa.setTabBarBadge.html) | 为 tabBar 某一项的右上角添加文本 |
| [qa.removeTabBarBadge](/api/ui/tab-bar/qa.removeTabBarBadge.html) | 移除 tabBar 某一项右上角的文本 |
| [qa.hideTabBarRedDot](/api/ui/tab-bar/qa.hideTabBarRedDot.html) | 隐藏 tabBar 某一项的右上角的红点 |
| [qa.hideTabBar](/api/ui/tab-bar/qa.hideTabBar.html) | 隐藏 tabBar |
### 字体
| 名称 | 功能说明 |
| ---------------------------------------------------- | ---------------- |
| [qa.loadFontFace](/api/ui/font/qa.loadFontFace.html) | 动态加载网络字体 |
### 下拉刷新
| 名称 | 功能说明 |
| --------------------------------------------------------------------------------- | -------------------- |
| [qa.stopPullDownRefresh](/api/ui/pull-down-refresh/qa.stopPullDownRefresh.html) | 停止当前页面下拉刷新 |
| [qa.startPullDownRefresh](/api/ui/pull-down-refresh/qa.startPullDownRefresh.html) | 开始下拉刷新 |
### 滚动
| 名称 | 功能说明 |
| ------------------------------------------------------ | -------------------- |
| [qa.pageScrollTo](/api/ui/scroll/qa.pageScrollTo.html) | 将页面滚动到目标位置 |
### 动画
| 名称 | 功能说明 |
| --- | --- |
| [qa.createAnimation](/api/ui/animation/qa.createAnimation.html) | 创建一个动画实例 [animation](/api/ui/animation/Animation.html) |
#### Animation
| 名称 | 功能说明 |
| --- | --- |
| [Animation.height](/api/ui/animation/Animation.height.html) | 设置高度 |
| [Animation.left](/api/ui/animation/Animation.left.html) | 设置 left 值 |
| [Animation.matrix](/api/ui/animation/Animation.matrix.html) | 同 [transform-function matrix](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix) |
| [Animation.matrix3d](/api/ui/animation/Animation.matrix3d.html) | 同 [transform-function matrix3d](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix3d) |
| [Animation.opacity](/api/ui/animation/Animation.opacity.html) | 设置透明度 |
| [Animation.right](/api/ui/animation/Animation.right.html) | 设置 right 值 |
| [Animation.rotate](/api/ui/animation/Animation.rotate.html) | 从原点顺时针旋转一个角度 |
| [Animation.rotate3d](/api/ui/animation/Animation.rotate3d.html) | 从 X 轴顺时针旋转一个角度 |
| [Animation.rotateX](/api/ui/animation/Animation.rotateX.html) | 从 X 轴顺时针旋转一个角度 |
| [Animation.rotateY](/api/ui/animation/Animation.rotateY.html) | 从 Y 轴顺时针旋转一个角度 |
| [Animation.rotateZ](/api/ui/animation/Animation.rotateZ.html) | 从 Z 轴顺时针旋转一个角度 |
| [Animation.scale](/api/ui/animation/Animation.scale.html) | 缩放 |
| [Animation.scale3d](/api/ui/animation/Animation.scale3d.html) | 缩放 |
| [Animation.scaleX](/api/ui/animation/Animation.scaleX.html) | 缩放 X 轴 |
| [Animation.scaleY](/api/ui/animation/Animation.scaleY.html) | 缩放 Y 轴 |
| [Animation.scaleZ](/api/ui/animation/Animation.scaleZ.html) | 缩放 Z 轴 |
| [Animation.skew](/api/ui/animation/Animation.skew.html) | 对 X、Y 轴坐标进行倾斜 |
| [Animation.skewX](/api/ui/animation/Animation.skewX.html) | 对 X 轴坐标进行倾斜 |
| [Animation.skewY](/api/ui/animation/Animation.skewY.html) | 对 Y 轴坐标进行倾斜 |
| [Animation.step](/api/ui/animation/Animation.step.html) | 表示一组动画完成 |
| [Animation.top](/api/ui/animation/Animation.top.html) | 设置 top 值 |
| [Animation.translate](/api/ui/animation/Animation.translate.html) | 平移变换 |
| [Animation.translate3d](/api/ui/animation/Animation.translate3d.html) | 对 xyz 坐标进行平移变换 |
| [Animation.translateX](/api/ui/animation/Animation.translateX.html) | 对 X 轴平移 |
| [Animation.translateY](/api/ui/animation/Animation.translateY.html) | 对 Y 轴平移 |
| [Animation.translateZ](/api/ui/animation/Animation.translateZ.html) | 对 Z 轴平移 |
| [Animation.width](/api/ui/animation/Animation.width.html) | 设置宽度 |
| [Animation.backgroundColor](/api/ui/animation/Animation.backgroundColor.html) | 设置背景色 |
| [Animation.bottom](/api/ui/animation/Animation.bottom.html) | 设置 bottom 值 |
| [Animation.export](/api/ui/animation/Animation.export.html) | 导出动画队列 |
### 自定义组件
| 名称 | 功能说明 |
| -------------------------------------------------------- | ---------------------------------- |
| [qa.nextTick](/api/ui/custom-component/qa.nextTick.html) | 延迟一部分操作到下一个时间片再执行 |
### 菜单
| 名称 | 功能说明 |
| --- | --- |
| [qa.getMenuButtonBoundingClientRect](/api/ui/menu/qa.getMenuButtonBoundingClientRect.html) | 获取菜单按钮(右上角menu按钮)的布局位置信息 |
## 网络
### 发起请求
| 名称 | 功能说明 |
| -------------------------------------------------- | ------------------- |
| [qa.request](/api/network/request/qa.request.html) | 发起 HTTPS 网络请求的接口 |
#### RequestTask
| 名称 | 功能说明 |
| ---------------------------------------------------------------- | ------------ |
| [RequestTask.abort](/api/network/request/RequestTask.abort.html) | 中断该请求任务 |
### 下载
| 名称 | 功能说明 |
| ------------------------------------------------------------- | ------------------ |
| [qa.downloadFile](/api/network/download/qa.downloadFile.html) | 下载文件资源到本地的接口 |
#### DownloadTask
| 名称 | 功能说明 |
| --- | --- |
| [DownloadTask.abort](/api/network/download/DownloadTask.abort.html) | 中断进行中的下载任务 |
| [DownloadTask.offProgressUpdate](/api/network/download/DownloadTask.offProgressUpdate.html) | 取消订阅**下载进度变化事件** |
| [DownloadTask.onProgressUpdate](/api/network/download/DownloadTask.onProgressUpdate.html) | 订阅**下载进度变化事件**事件 |
### 上传
| 名称 | 功能说明 |
| ------------------------------------------------------- | ---------------------- |
| [qa.uploadFile](/api/network/upload/qa.uploadFile.html) | 将本地文件上传到服务器端 |
#### UploadTask
| 名称 | 功能说明 |
| ------------------------------------------------------------------------------------- | ------------------------ |
| [UploadTask.abort](/api/network/upload/UploadTask.abort.html) | 中断该上传任务 |
| [UploadTask.offProgressUpdate](/api/network/upload/UploadTask.offProgressUpdate.html) | 取消订阅**上传进度变化事件** |
| [UploadTask.onProgressUpdate](/api/network/upload/UploadTask.onProgressUpdate.html) | 订阅**上传进度变化事件**回调 |
### WebSocket
| 名称 | 功能说明 |
| ------------------------------------------------------------------------ | ------------------------------------- |
| [qa.sendSocketMessage](/api/network/websocket/qa.sendSocketMessage.html) | 通过已连接成功的WebSocket连接发送数据 |
| [qa.onSocketOpen](/api/network/websocket/qa.onSocketOpen.html) | 订阅**WebSocket连接打开事件**回调 |
| [qa.onSocketMessage](/api/network/websocket/qa.onSocketMessage.html) | 订阅**WebSocket接受到服务器的消息事件**回调 |
| [qa.onSocketError](/api/network/websocket/qa.onSocketError.html) | 订阅**WebSocket错误事件**回调 |
| [qa.onSocketClose](/api/network/websocket/qa.onSocketClose.html) | 订阅**WebSocket连接关闭事件**回调 |
| [qa.connectSocket](/api/network/websocket/qa.connectSocket.html) | 创建新的WebSocket连接 |
| [qa.closeSocket](/api/network/websocket/qa.closeSocket.html) | 关闭WebSocket连接 |
#### SocketTask
| 名称 | 功能说明 |
| ------------------------------------------------------------------------ | ------------------------------------- |
| [SocketTask.close](/api/network/websocket/SocketTask.close.html) | 关闭当前WebSocket连接 |
| [SocketTask.onClose](/api/network/websocket/SocketTask.onClose.html) | 订阅**WebSocket连接关闭事件**回调 |
| [SocketTask.onError](/api/network/websocket/SocketTask.onError.html) | 订阅**WebSocket错误事件**回调 |
| [SocketTask.onMessage](/api/network/websocket/SocketTask.onMessage.html) | 订阅**WebSocket接受到服务器的消息事件**回调 |
| [SocketTask.onOpen](/api/network/websocket/SocketTask.onOpen.html) | 订阅**WebSocket连接打开事件**回调 |
| [SocketTask.send](/api/network/websocket/SocketTask.send.html) | 通过已创建的WebSocket连接发送数据 |
## 数据缓存
| 名称 | 功能说明 |
| --- | --- |
| [qa.setStorageSync](/api/storage/qa.setStorageSync.html) | [qa.setStorage](/api/storage/qa.setStorage.html) 的同步版本 |
| [qa.setStorage](/api/storage/qa.setStorage.html) | 将数据存储在本地缓存中指定的 key 中 |
| [qa.removeStorageSync](/api/storage/qa.removeStorageSync.html) | [qa.removeStorage](/api/storage/qa.removeStorage.html) 的同步版本 |
| [qa.removeStorage](/api/storage/qa.removeStorage.html) | 从本地缓存中移除指定 key |
| [qa.getStorageSync](/api/storage/qa.getStorageSync.html) | [qa.getStorage](/api/storage/qa.getStorage.html) 的同步版本 |
| [qa.getStorageInfoSync](/api/storage/qa.getStorageInfoSync.html) | [qa.getStorageInfo](/api/storage/qa.getStorageInfo.html) 的同步版本 |
| [qa.getStorageInfo](/api/storage/qa.getStorageInfo.html) | 异步获取当前 storage 的相关信息 |
| [qa.getStorage](/api/storage/qa.getStorage.html) | 从本地缓存中异步获取指定 key 的内容 |
| [qa.clearStorageSync](/api/storage/qa.clearStorageSync.html) | [qa.clearStorage](/api/storage/qa.clearStorage.html) 的同步版本 |
| [qa.clearStorage](/api/storage/qa.clearStorage.html) | 清理本地数据缓存 |
## 媒体
### 地图
| 名称 | 功能说明 |
| --- | --- |
| [qa.createMapContext](/api/media/map/qa.createMapContext.html) | 创建 [map](/api/../component/map.html) 上下文 [MapContext](/api/media/map/MapContext.html) 对象 |
#### MapContext
| 名称 | 功能说明 |
| -------------------------------------------------------------------------------- | -------------------------- |
| [MapContext.getCenterLocation](/api/media/map/MapContext.getCenterLocation.html) | 获取当前地图中心的经纬度 |
| [MapContext.getRegion](/api/media/map/MapContext.getRegion.html) | 获取当前地图的视野范围 |
| [MapContext.getScale](/api/media/map/MapContext.getScale.html) | 获取当前地图的缩放级别 |
| [MapContext.includePoints](/api/media/map/MapContext.includePoints.html) | 缩放视野展示所有经纬度 |
| [MapContext.moveToLocation](/api/media/map/MapContext.moveToLocation.html) | 将地图中心移动到当前定位点 |
| [MapContext.translateMarker](/api/media/map/MapContext.translateMarker.html) | 平移 marker,带动画 |
### 图片
| 名称 | 功能说明 |
| ---------------------------------------------------------------------------- | -------------------------------- |
| [qa.saveImageToPhotosAlbum](/api/media/image/qa.saveImageToPhotosAlbum.html) | 保存图片到系统相册 |
| [qa.previewImage](/api/media/image/qa.previewImage.html) | 在新页面中全屏预览图片 |
| [qa.getImageInfo](/api/media/image/qa.getImageInfo.html) | 获取图片信息 |
| [qa.compressImage](/api/media/image/qa.compressImage.html) | 压缩图片接口,可选压缩质量 |
| [qa.chooseImage](/api/media/image/qa.chooseImage.html) | 从本地相册选择图片或使用相机拍照 |
### 视频
| 名称 | 功能说明 |
| --- | --- |
| [qa.saveVideoToPhotosAlbum](/api/media/video/qa.saveVideoToPhotosAlbum.html) | 保存视频到系统相册 |
| [qa.createVideoContext](/api/media/video/qa.createVideoContext.html) | 创建 [video](/api/../component/video.html) 上下文 [VideoContext](/api/media/video/VideoContext.html) 对象 |
| [qa.chooseVideo](/api/media/video/qa.chooseVideo.html) | 拍摄视频或从手机相册中选视频 |
#### VideoContext
| 名称 | 功能说明 |
| -------------------------------------------------------------------------------------- | -------------- |
| [VideoContext.exitFullScreen](/api/media/video/VideoContext.exitFullScreen.html) | 退出全屏 |
| [VideoContext.pause](/api/media/video/VideoContext.pause.html) | 暂停视频 |
| [VideoContext.play](/api/media/video/VideoContext.play.html) | 播放视频 |
| [VideoContext.requestFullScreen](/api/media/video/VideoContext.requestFullScreen.html) | 进入全屏 |
| [VideoContext.seek](/api/media/video/VideoContext.seek.html) | 跳转到指定位置 |
| [VideoContext.stop](/api/media/video/VideoContext.stop.html) | 停止视频 |
### 音频
| 名称 | 功能说明 |
| --- | --- |
| [qa.setInnerAudioOption](/api/media/audio/qa.setInnerAudioOption.html) | 设置 [InnerAudioContext](/api/media/audio/InnerAudioContext.html) 的播放选项 |
| [qa.createInnerAudioContext](/api/media/audio/qa.createInnerAudioContext.html) | 创建用于音频播放 [audio](../../../component/audio.html) 的上下文 [InnerAudioContext](/api/media/audio/InnerAudioContext.html) 对象 |
#### InnerAudioContext
| 名称 | 功能说明 |
| --- | --- |
| [InnerAudioContext.destroy](/api/media/audio/InnerAudioContext.destroy.html) | 销毁当前InnerAudioContext实例 |
| [InnerAudioContext.offCanplay](/api/media/audio/InnerAudioContext.offCanplay.html) | 取消订阅**音频进入可以播放状态事件** |
| [InnerAudioContext.offEnded](/api/media/audio/InnerAudioContext.offEnded.html) | 取消订阅**音频自然播放至结束的事件** |
| [InnerAudioContext.offError](/api/media/audio/InnerAudioContext.offError.html) | 取消订阅**音频播放错误事件** |
| [InnerAudioContext.offPause](/api/media/audio/InnerAudioContext.offPause.html) | 取消订阅**音频暂停事件** |
| [InnerAudioContext.offPlay](/api/media/audio/InnerAudioContext.offPlay.html) | 取消订阅**音频播放事件** |
| [InnerAudioContext.offSeeked](/api/media/audio/InnerAudioContext.offSeeked.html) | 取消订阅**音频完成跳转操作的事件** |
| [InnerAudioContext.offSeeking](/api/media/audio/InnerAudioContext.offSeeking.html) | 取消订阅**音频进行跳转操作的事件** |
| [InnerAudioContext.offStop](/api/media/audio/InnerAudioContext.offStop.html) | 取消订阅**音频停止事件** |
| [InnerAudioContext.offTimeUpdate](/api/media/audio/InnerAudioContext.offTimeUpdate.html) | 取消订阅**音频播放进度更新事件** |
| [InnerAudioContext.offWaiting](/api/media/audio/InnerAudioContext.offWaiting.html) | 取消订阅**音频加载中事件** |
| [InnerAudioContext.onCanplay](/api/media/audio/InnerAudioContext.onCanplay.html) | 订阅**音频进入可以播放状态的事件**回调 |
| [InnerAudioContext.onEnded](/api/media/audio/InnerAudioContext.onEnded.html) | 订阅**音频自然播放至结束的事件**回调 |
| [InnerAudioContext.onError](/api/media/audio/InnerAudioContext.onError.html) | 订阅**音频播放错误事件**回调 |
| [InnerAudioContext.onPause](/api/media/audio/InnerAudioContext.onPause.html) | 订阅**音频暂停事件**回调 |
| [InnerAudioContext.onPlay](/api/media/audio/InnerAudioContext.onPlay.html) | 订阅**音频播放事件**回调 |
| [InnerAudioContext.onSeeked](/api/media/audio/InnerAudioContext.onSeeked.html) | 订阅**音频完成跳转操作的事件**回调 |
| [InnerAudioContext.onSeeking](/api/media/audio/InnerAudioContext.onSeeking.html) | 订阅**音频进行跳转操作的事件**回调 |
| [InnerAudioContext.onStop](/api/media/audio/InnerAudioContext.onStop.html) | 订阅**音频停止事件**回调 |
| [InnerAudioContext.onTimeUpdate](/api/media/audio/InnerAudioContext.onTimeUpdate.html) | 订阅**音频播放进度更新事件**回调 |
| [InnerAudioContext.onWaiting](/api/media/audio/InnerAudioContext.onWaiting.html) | 订阅**音频加载中事件**回调 |
| [InnerAudioContext.pause](/api/media/audio/InnerAudioContext.pause.html) | 暂停播放音频 |
| [InnerAudioContext.play](/api/media/audio/InnerAudioContext.play.html) | 播放音频 |
| [InnerAudioContext.seek](/api/media/audio/InnerAudioContext.seek.html) | 跳转到该音频的指定位置 |
| [InnerAudioContext.stop](/api/media/audio/InnerAudioContext.stop.html) | 停止播放音频 |
### 背景音频
| 名称 | 功能说明 |
| --- | --- |
| [qa.stopBackgroundAudio](/api/media/background-audio/qa.stopBackgroundAudio.html) | 停止播放音乐 |
| [qa.seekBackgroundAudio](/api/media/background-audio/qa.seekBackgroundAudio.html) | 控制音乐播放进度 |
| [qa.playBackgroundAudio](/api/media/background-audio/qa.playBackgroundAudio.html) | 使用后台播放器播放音乐 |
| [qa.pauseBackgroundAudio](/api/media/background-audio/qa.pauseBackgroundAudio.html) | 暂停播放音乐 |
| [qa.onBackgroundAudioStop](/api/media/background-audio/qa.onBackgroundAudioStop.html) | 监听音乐停止事件 |
| [qa.onBackgroundAudioPlay](/api/media/background-audio/qa.onBackgroundAudioPlay.html) | 监听音乐播放事件 |
| [qa.onBackgroundAudioPause](/api/media/background-audio/qa.onBackgroundAudioPause.html) | 监听音乐暂停事件 |
| [qa.getBackgroundAudioPlayerState](/api/media/background-audio/qa.getBackgroundAudioPlayerState.html) | 获取后台音乐播放状态 |
| [qa.getBackgroundAudioManager](/api/media/background-audio/qa.getBackgroundAudioManager.html) | 获取**全局唯一**的背景音频管理器 |
#### BackgroundAudioManager
| 名称 | 功能说明 |
| --- | --- |
| [BackgroundAudioManager.onPlay](/api/media/background-audio/BackgroundAudioManager.onPlay.html) | 监听背景音频播放事件 |
| [BackgroundAudioManager.onSeeked](/api/media/background-audio/BackgroundAudioManager.onSeeked.html) | 监听背景音频完成跳转操作事件 |
| [BackgroundAudioManager.onSeeking](/api/media/background-audio/BackgroundAudioManager.onSeeking.html) | 监听背景音频开始跳转操作事件 |
| [BackgroundAudioManager.onStop](/api/media/background-audio/BackgroundAudioManager.onStop.html) | 监听背景音频停止事件 |
| [BackgroundAudioManager.onTimeUpdate](/api/media/background-audio/BackgroundAudioManager.onTimeUpdate.html) | 监听背景音频播放进度更新事件,只有快应用在前台时会回调 |
| [BackgroundAudioManager.onWaiting](/api/media/background-audio/BackgroundAudioManager.onWaiting.html) | 监听音频加载中事件 |
| [BackgroundAudioManager.pause](/api/media/background-audio/BackgroundAudioManager.pause.html) | 暂停音乐 |
| [BackgroundAudioManager.play](/api/media/background-audio/BackgroundAudioManager.play.html) | 播放音乐 |
| [BackgroundAudioManager.seek](/api/media/background-audio/BackgroundAudioManager.seek.html) | 跳转到指定位置 |
| [BackgroundAudioManager.stop](/api/media/background-audio/BackgroundAudioManager.stop.html) | 停止音乐 |
| [BackgroundAudioManager.onCanplay](/api/media/background-audio/BackgroundAudioManager.onCanplay.html) | 监听背景音频进入可播放状态事件 |
| [BackgroundAudioManager.onEnded](/api/media/background-audio/BackgroundAudioManager.onEnded.html) | 监听背景音频自然播放结束事件 |
| [BackgroundAudioManager.onError](/api/media/background-audio/BackgroundAudioManager.onError.html) | 监听背景音频播放错误事件 |
| [BackgroundAudioManager.onPause](/api/media/background-audio/BackgroundAudioManager.onPause.html) | 监听背景音频暂停事件 |
### 录音
| 名称 | 功能说明 |
| --- | --- |
| [qa.stopRecord](/api/media/recorder/qa.stopRecord.html) | 停止录音 |
| [qa.startRecord](/api/media/recorder/qa.startRecord.html) | 开始录音 |
| [qa.getRecorderManager](/api/media/recorder/qa.getRecorderManager.html) | 获取**全局唯一**的录音管理器 RecorderManager |
#### RecorderManager
| 名称 | 功能说明 |
| ----------------------------------------------------------------------------- | ---------------- |
| [RecorderManager.onError](/api/media/recorder/RecorderManager.onError.html) | 订阅**录音错误事件**回调 |
| [RecorderManager.onPause](/api/media/recorder/RecorderManager.onPause.html) | 订阅**录音暂停事件**回调(只支持 Android 7.0 及以上设备) |
| [RecorderManager.onResume](/api/media/recorder/RecorderManager.onResume.html) | 订阅**录音继续事件**回调(只支持 Android 7.0 及以上设备) |
| [RecorderManager.onStart](/api/media/recorder/RecorderManager.onStart.html) | 订阅**录音开始事件**回调 |
| [RecorderManager.onStop](/api/media/recorder/RecorderManager.onStop.html) | 订阅**录音结束事件**回调 |
| [RecorderManager.pause](/api/media/recorder/RecorderManager.pause.html) | 暂停录音 (只支持 Android 7.0 及以上设备) |
| [RecorderManager.resume](/api/media/recorder/RecorderManager.resume.html) | 继续录音 (只支持 Android 7.0 及以上设备) |
| [RecorderManager.start](/api/media/recorder/RecorderManager.start.html) | 开始录音 |
| [RecorderManager.stop](/api/media/recorder/RecorderManager.stop.html) | 停止录音 |
## 位置
| 名称 | 功能说明 |
| --------------------------------------------------------- | -------------------------- |
| [qa.openLocation](/api/location/qa.openLocation.html) | 使用快应用内置地图查看位置 |
| [qa.getLocation](/api/location/qa.getLocation.html) | 获取当前的地理位置、速度 |
| [qa.chooseLocation](/api/location/qa.chooseLocation.html) | 打开地图选择位置 |
## 画布
| 名称 | 功能说明 |
| --- | --- |
| [qa.createCanvasContext](/api/canvas/qa.createCanvasContext.html) | 创建 canvas 的绘图上下文 [CanvasContext](/api/canvas/CanvasContext.html) 对象 |
| [qa.canvasPutImageData](/api/canvas/qa.canvasPutImageData.html) | 将像素数据绘制到画布 |
| [qa.canvasGetImageData](/api/canvas/qa.canvasGetImageData.html) | 获取 canvas 区域隐含的像素数据 |
### CanvasContext
| 名称 | 功能说明 |
| --- | --- |
| [CanvasContext.arc](/api/canvas/CanvasContext.arc.html) | 创建一条弧线 |
| [CanvasContext.arcTo](/api/canvas/CanvasContext.arcTo.html) | 根据控制点和半径绘制圆弧路径 |
| [CanvasContext.beginPath](/api/canvas/CanvasContext.beginPath.html) | 开始创建一个路径 |
| [CanvasContext.bezierCurveTo](/api/canvas/CanvasContext.bezierCurveTo.html) | 创建三次方贝塞尔曲线路径 |
| [CanvasContext.clearRect](/api/canvas/CanvasContext.clearRect.html) | 清除画布上在该矩形区域内的内容 |
| [CanvasContext.clip](/api/canvas/CanvasContext.clip.html) | 从原始画布中剪切任意形状和尺寸 |
| [CanvasContext.closePath](/api/canvas/CanvasContext.closePath.html) | 关闭一个路径 |
| [CanvasContext.createCircularGradient](/api/canvas/CanvasContext.createCircularGradient.html) | 创建一个圆形的渐变颜色 |
| [CanvasContext.createLinearGradient](/api/canvas/CanvasContext.createLinearGradient.html) | 创建一个线性的渐变颜色 |
| [CanvasContext.createPattern](/api/canvas/CanvasContext.createPattern.html) | 对指定的图像创建模式的方法,可在指定的方向上重复元图像 |
| [CanvasContext.draw](/api/canvas/CanvasContext.draw.html) | 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中 |
| [CanvasContext.drawImage](/api/canvas/CanvasContext.drawImage.html) | 绘制图像到画布 |
| [CanvasContext.fill](/api/canvas/CanvasContext.fill.html) | 对当前路径中的内容进行填充 |
| [CanvasContext.fillRect](/api/canvas/CanvasContext.fillRect.html) | 填充一个矩形 |
| [CanvasContext.fillText](/api/canvas/CanvasContext.fillText.html) | 在画布上绘制被填充的文本 |
| [CanvasContext.lineTo](/api/canvas/CanvasContext.lineTo.html) | 增加一个新点,然后创建一条从上次指定点到目标点的线 |
| [CanvasContext.measureText](/api/canvas/CanvasContext.measureText.html) | 测量文本尺寸信息 |
| [CanvasContext.moveTo](/api/canvas/CanvasContext.moveTo.html) | 把路径移动到画布中的指定点,不创建线条 |
| [CanvasContext.quadraticCurveTo](/api/canvas/CanvasContext.quadraticCurveTo.html) | 创建二次贝塞尔曲线路径 |
| [CanvasContext.rect](/api/canvas/CanvasContext.rect.html) | 创建一个矩形路径 |
| [CanvasContext.restore](/api/canvas/CanvasContext.restore.html) | 恢复之前保存的绘图上下文 |
| [CanvasContext.rotate](/api/canvas/CanvasContext.rotate.html) | 以原点为中心顺时针旋转当前坐标轴 |
| [CanvasContext.save](/api/canvas/CanvasContext.save.html) | 保存绘图上下文 |
| [CanvasContext.scale](/api/canvas/CanvasContext.scale.html) | 在调用后,之后创建的路径其横纵坐标会被缩放 |
| [CanvasContext.setFillStyle](/api/canvas/CanvasContext.setFillStyle.html) | 设置填充色 |
| [CanvasContext.setFontSize](/api/canvas/CanvasContext.setFontSize.html) | 设置字体的字号 |
| [CanvasContext.setGlobalAlpha](/api/canvas/CanvasContext.setGlobalAlpha.html) | 设置全局画笔透明度 |
| [CanvasContext.setLineCap](/api/canvas/CanvasContext.setLineCap.html) | 设置线条的端点样式 |
| [CanvasContext.setLineDash](/api/canvas/CanvasContext.setLineDash.html) | 设置虚线样式 |
| [CanvasContext.setLineJoin](/api/canvas/CanvasContext.setLineJoin.html) | 设置线条的交点样式 |
| [CanvasContext.setLineWidth](/api/canvas/CanvasContext.setLineWidth.html) | 设置线条的宽度 |
| [CanvasContext.setMiterLimit](/api/canvas/CanvasContext.setMiterLimit.html) | 设置最大斜接长度 |
| [CanvasContext.setStrokeStyle](/api/canvas/CanvasContext.setStrokeStyle.html) | 设置描边颜色 |
| [CanvasContext.setTextAlign](/api/canvas/CanvasContext.setTextAlign.html) | 设置文字的对齐 |
| [CanvasContext.setTextBaseline](/api/canvas/CanvasContext.setTextBaseline.html) | 设置文字的竖直对齐 |
| [CanvasContext.setTransform](/api/canvas/CanvasContext.setTransform.html) | 使用矩阵重新设置(覆盖)当前变换的方法 |
| [CanvasContext.stroke](/api/canvas/CanvasContext.stroke.html) | 画出当前路径的边框 |
| [CanvasContext.strokeRect](/api/canvas/CanvasContext.strokeRect.html) | 画一个矩形(非填充) |
| [CanvasContext.strokeText](/api/canvas/CanvasContext.strokeText.html) | 给定的 (x, y) 位置绘制文本描边的方法 |
| [CanvasContext.transform](/api/canvas/CanvasContext.transform.html) | 使用矩阵多次叠加当前变换的方法 |
| [CanvasContext.translate](/api/canvas/CanvasContext.translate.html) | 对当前坐标系的原点 (0, 0) 进行变换 |
### CanvasGradient
| 名称 | 功能说明 |
| --------------------------------------------------------------------------- | ---------------- |
| [CanvasGradient.addColorStop](/api/canvas/CanvasGradient.addColorStop.html) | 添加颜色的渐变点 |
| [Image](/api/canvas/Image.html) | 图片对象 |
## 文件
| 名称 | 功能说明 |
| --------------------------------------------------------- | -------------------------------------- |
| [qa.saveFile](/api/file/qa.saveFile.html) | 保存文件到本地 |
| [qa.removeSavedFile](/api/file/qa.removeSavedFile.html) | 删除本地缓存文件 |
| [qa.openDocument](/api/file/qa.openDocument.html) | 新开页面打开文档 |
| [qa.getSavedFileList](/api/file/qa.getSavedFileList.html) | 获取该快应用下已保存的本地缓存文件列表 |
| [qa.getSavedFileInfo](/api/file/qa.getSavedFileInfo.html) | 获取本地文件的文件信息 |
| [qa.getFileInfo](/api/file/qa.getFileInfo.html) | 获取文件信息 |
## 开放接口
### alipay
| 名称 | 功能说明 |
| ---------------------------------------------------------------------- | ------------------ |
| [qa.requestAliPayment](/api/open-api/alipay/qa.requestAliPayment.html) | 使用支付宝完成支付 |
### 登录
| 名称 | 功能说明 |
| ----------------------------------------------------------- | ---------------------------- |
| [qa.login](/api/open-api/login/qa.login.html) | 调用接口获取登录凭证(code) |
### 快应用跳转
| 名称 | 功能说明 |
| ------------------------------------------------------------------------------------------ | ------------------ |
| [qa.navigateToQuickapp](/api/open-api/quickapp-navigate/qa.navigateToQuickapp.html) | 打开另一个快应用 |
| [qa.navigateBackQuickapp](/api/open-api/quickapp-navigate/qa.navigateBackQuickapp.html) | 返回到上一个快应用 |
### 用户信息
| 名称 | 功能说明 |
| ------------------------------------------------------------- | ------------ |
| [qa.getUserInfo](/api/open-api/user-info/qa.getUserInfo.html) | 获取用户信息 |
### 授权
| 名称 | 功能说明 |
| --------------------------------------------------------- | ---------------------- |
| [qa.authorize](/api/open-api/authorize/qa.authorize.html) | 提前向用户发起授权请求 |
### 设置
| 名称 | 功能说明 |
| --- | --- |
| [qa.getSetting](/api/open-api/setting/qa.getSetting.html) | 获取用户的当前设置 |
| [AuthSetting](/api/open-api/setting/AuthSetting.html) | 用户授权设置信息,详情参考[权限](/api/../framework/open-ability/authorize.html) |
### vendorpay
| 名称 | 功能说明 |
| --- | --- |
| [qa.getVendorPaymentProvider](/api/open-api/vendorpay/qa.getVendorPaymentProvider.html) | 获取服务提供商,同步接口 |
| [qa.requestVendorPayment](/api/open-api/vendorpay/qa.requestVendorPayment.html) | 使用设备商提供的支付完成付款 |
### wxpay
| 名称 | 功能说明 |
| ------------------------------------------------------------------- | ------------------------------ |
| [qa.getWxPaymentType](/api/open-api/wxpay/qa.getWxPaymentType.html) | 获取当前可用的微信支付调用方式 |
| [qa.requestWxPayment](/api/open-api/wxpay/qa.requestWxPayment.html) | 发起微信支付 |
## 设备
### Wi-Fi
| 名称 | 功能说明 |
| ---------------------------------------------------------------- | ----------------------------- |
| [qa.stopWifi](/api/device/wifi/qa.stopWifi.html) | 关闭 Wi-Fi 模块 |
| [qa.startWifi](/api/device/wifi/qa.startWifi.html) | 初始化 Wi-Fi 模块 |
| [qa.onWifiConnected](/api/device/wifi/qa.onWifiConnected.html) | 监听连接上 Wi-Fi 的事件 |
| [qa.onGetWifiList](/api/device/wifi/qa.onGetWifiList.html) | 监听获取到 Wi-Fi 列表数据事件 |
| [qa.getWifiList](/api/device/wifi/qa.getWifiList.html) | 请求获取 Wi-Fi 列表 |
| [qa.getConnectedWifi](/api/device/wifi/qa.getConnectedWifi.html) | 获取已连接中的 Wi-Fi 信息 |
| [qa.connectWifi](/api/device/wifi/qa.connectWifi.html) | 连接 Wi-Fi |
| [WifiInfo](/api/device/wifi/WifiInfo.html) | Wifi 信息 |
### 低功耗蓝牙
| 名称 | 功能说明 |
| --- | --- |
| [qa.readBLECharacteristicValue](/api/device/bluetooth-ble/qa.readBLECharacteristicValue.html) | 读取低功耗蓝牙设备的特征值的二进制数据值 |
| [qa.onBLEConnectionStateChange](/api/device/bluetooth-ble/qa.onBLEConnectionStateChange.html) | 监听低功耗蓝牙连接状态的改变事件 |
| [qa.onBLECharacteristicValueChange](/api/device/bluetooth-ble/qa.onBLECharacteristicValueChange.html) | 监听低功耗蓝牙设备的特征值变化事件 |
| [qa.notifyBLECharacteristicValueChange](/api/device/bluetooth-ble/qa.notifyBLECharacteristicValueChange.html) | 启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值 |
| [qa.getBLEDeviceServices](/api/device/bluetooth-ble/qa.getBLEDeviceServices.html) | 获取蓝牙设备所有服务(service) |
| [qa.getBLEDeviceCharacteristics](/api/device/bluetooth-ble/qa.getBLEDeviceCharacteristics.html) | 获取蓝牙设备某个服务中所有特征值(characteristic) |
| [qa.createBLEConnection](/api/device/bluetooth-ble/qa.createBLEConnection.html) | 连接低功耗蓝牙设备 |
| [qa.closeBLEConnection](/api/device/bluetooth-ble/qa.closeBLEConnection.html) | 断开与低功耗蓝牙设备的连接 |
| [qa.writeBLECharacteristicValue](/api/device/bluetooth-ble/qa.writeBLECharacteristicValue.html) | 向低功耗蓝牙设备特征值中写入二进制数据 |
### 联系人
| 名称 | 功能说明 |
| ----------------------------------------------------------------- | -------------------- |
| [qa.addPhoneContact](/api/device/contact/qa.addPhoneContact.html) | 添加手机通讯录联系人 |
### 蓝牙
| 名称 | 功能说明 |
| --- | --- |
| [qa.stopBluetoothDevicesDiscovery](/api/device/bluetooth/qa.stopBluetoothDevicesDiscovery.html) | 停止搜寻附近的蓝牙外围设备 |
| [qa.startBluetoothDevicesDiscovery](/api/device/bluetooth/qa.startBluetoothDevicesDiscovery.html) | 开始搜寻附近的蓝牙外围设备 |
| [qa.openBluetoothAdapter](/api/device/bluetooth/qa.openBluetoothAdapter.html) | 初始化蓝牙模块 |
| [qa.onBluetoothDeviceFound](/api/device/bluetooth/qa.onBluetoothDeviceFound.html) | 监听寻找到新设备的事件 |
| [qa.onBluetoothAdapterStateChange](/api/device/bluetooth/qa.onBluetoothAdapterStateChange.html) | 监听蓝牙适配器状态变化事件 |
| [qa.getConnectedBluetoothDevices](/api/device/bluetooth/qa.getConnectedBluetoothDevices.html) | 根据 uuid 获取处于已连接状态的设备 |
| [qa.getBluetoothDevices](/api/device/bluetooth/qa.getBluetoothDevices.html) | 获取在蓝牙模块生效期间所有已发现的蓝牙设备 |
| [qa.getBluetoothAdapterState](/api/device/bluetooth/qa.getBluetoothAdapterState.html) | 获取本机蓝牙适配器状态 |
| [qa.closeBluetoothAdapter](/api/device/bluetooth/qa.closeBluetoothAdapter.html) | 关闭蓝牙模块 |
### 电量
| 名称 | 功能说明 |
| --- | --- |
| [qa.getBatteryInfoSync](/api/device/battery/qa.getBatteryInfoSync.html) | [qa.getBatteryInfo](/api/device/battery/qa.getBatteryInfo.html) 的同步版本 |
| [qa.getBatteryInfo](/api/device/battery/qa.getBatteryInfo.html) | 获取设备电量 |
### 剪贴板
| 名称 | 功能说明 |
| --------------------------------------------------------------------- | -------------------- |
| [qa.setClipboardData](/api/device/clipboard/qa.setClipboardData.html) | 设置系统剪贴板的内容 |
| [qa.getClipboardData](/api/device/clipboard/qa.getClipboardData.html) | 获取系统剪贴板的内容 |
### 网络
| 名称 | 功能说明 |
| ----------------------------------------------------------------------------- | -------------------- |
| [qa.onNetworkStatusChange](/api/device/network/qa.onNetworkStatusChange.html) | 监听网络状态变化事件 |
| [qa.getNetworkType](/api/device/network/qa.getNetworkType.html) | 获取网络类型 |
### 屏幕
| 名称 | 功能说明 |
| ------------------------------------------------------------------------ | -------------------- |
| [qa.setScreenBrightness](/api/device/screen/qa.setScreenBrightness.html) | 设置屏幕亮度 |
| [qa.setKeepScreenOn](/api/device/screen/qa.setKeepScreenOn.html) | 设置是否保持常亮状态 |
| [qa.onUserCaptureScreen](/api/device/screen/qa.onUserCaptureScreen.html) | 监听用户主动截屏事件 |
| [qa.getScreenBrightness](/api/device/screen/qa.getScreenBrightness.html) | 获取屏幕亮度 |
### 电话
| 名称 | 功能说明 |
| ----------------------------------------------------------- | -------- |
| [qa.makePhoneCall](/api/device/phone/qa.makePhoneCall.html) | 拨打电话 |
### 加速计
| 名称 | 功能说明 |
| ----------------------------------------------------------------------------------- | ------------------ |
| [qa.stopAccelerometer](/api/device/accelerometer/qa.stopAccelerometer.html) | 关闭加速度计监听器 |
| [qa.startAccelerometer](/api/device/accelerometer/qa.startAccelerometer.html) | 启动监听加速度计数据 |
| [qa.onAccelerometerChange](/api/device/accelerometer/qa.onAccelerometerChange.html) | 注册加速度数据回调监听器 |
### 罗盘
| 名称 | 功能说明 |
| ----------------------------------------------------------------- | -------------------- |
| [qa.stopCompass](/api/device/compass/qa.stopCompass.html) | 关闭罗盘监听器 |
| [qa.startCompass](/api/device/compass/qa.startCompass.html) | 启动监听罗盘数据 |
| [qa.onCompassChange](/api/device/compass/qa.onCompassChange.html) | 注册罗盘数据回调监听器 |
### 设备方向
| 名称 | 功能说明 |
| -------------------------------------------------------------------------------------- | ---------------------- |
| [qa.stopDeviceMotionListening](/api/device/motion/qa.stopDeviceMotionListening.html) | 关闭设备方向监听器 |
| [qa.startDeviceMotionListening](/api/device/motion/qa.startDeviceMotionListening.html) | 启动监听设备方向数据 |
| [qa.onDeviceMotionChange](/api/device/motion/qa.onDeviceMotionChange.html) | 注册设备方向变化数据回调监听器 |
### 陀螺仪
| 名称 | 功能说明 |
| ----------------------------------------------------------------------- | ---------------------- |
| [qa.stopGyroscope](/api/device/gyroscope/qa.stopGyroscope.html) | 关闭陀螺仪计监听器 |
| [qa.startGyroscope](/api/device/gyroscope/qa.startGyroscope.html) | 启动监听陀螺仪数据 |
| [qa.onGyroscopeChange](/api/device/gyroscope/qa.onGyroscopeChange.html) | 注册陀螺仪数据回调监听器 |
### 性能
| 名称 | 功能说明 |
| --------------------------------------------------------------------- | -------------------- |
| [qa.onMemoryWarning](/api/device/performance/qa.onMemoryWarning.html) | 监听内存不足告警事件 |
### 扫码
| 名称 | 功能说明 |
| ------------------------------------------------ | -------------------------- |
| [qa.scanCode](/api/device/scan/qa.scanCode.html) | 调起客户端扫码界面进行扫码 |
### 振动
| 名称 | 功能说明 |
| ----------------------------------------------------------- | --------------------------------- |
| [qa.vibrateShort](/api/device/vibrate/qa.vibrateShort.html) | 使手机发生较短时间的振动(15 ms) |
| [qa.vibrateLong](/api/device/vibrate/qa.vibrateLong.html) | 使手机发生较长时间的振动(400 ms) |
## QXML
| 名称 | 功能说明 |
| --- | --- |
| [qa.createSelectorQuery](/api/qxml/qa.createSelectorQuery.html) | 返回一个 SelectorQuery 对象实例 |
| [qa.createIntersectionObserver](/api/qxml/qa.createIntersectionObserver.html) | 创建并返回一个 IntersectionObserver 对象实例 |
### IntersectionObserver
| 名称 | 功能说明 |
| --- | --- |
| [IntersectionObserver.disconnect](/api/qxml/IntersectionObserver.disconnect.html) | 停止监听 |
| [IntersectionObserver.observe](/api/qxml/IntersectionObserver.observe.html) | 指定目标节点并开始监听相交状态变化情况 |
| [IntersectionObserver.relativeTo](/api/qxml/IntersectionObserver.relativeTo.html) | 使用选择器指定一个节点,作为参照区域之一 |
| [IntersectionObserver.relativeToViewport](/api/qxml/IntersectionObserver.relativeToViewport.html) | 指定页面显示区域作为参照区域之一 |
### NodesRef
| 名称 | 功能说明 |
| ------------------------------------------------------------------------- | ------------------------------- |
| [NodesRef.boundingClientRect](/api/qxml/NodesRef.boundingClientRect.html) | 添加节点的布局位置的查询请求 |
| [NodesRef.context](/api/qxml/NodesRef.context.html) | 添加节点的 Context 对象查询请求 |
| [NodesRef.fields](/api/qxml/NodesRef.fields.html) | 获取节点的相关信息 |
| [NodesRef.scrollOffset](/api/qxml/NodesRef.scrollOffset.html) | 添加节点的滚动位置查询请求 |
### SelectorQuery
| 名称 | 功能说明 |
| --- | --- |
| [SelectorQuery.selectViewport](/api/qxml/SelectorQuery.selectViewport.html) | 选择显示区域 |
| [SelectorQuery.exec](/api/qxml/SelectorQuery.exec.html) | 执行所有的请求 |
| [SelectorQuery.in](/api/qxml/SelectorQuery.in.html) | 将选择器的选取范围更改为自定义组件 `component` 内 |
| [SelectorQuery.select](/api/qxml/SelectorQuery.select.html) | 在当前页面下选择第一个匹配选择器 `selector` 的节点 |
| [SelectorQuery.selectAll](/api/qxml/SelectorQuery.selectAll.html) | 在当前页面下选择匹配选择器 selector 的所有节点 |
# qa.getLaunchOptionsSync()
获取快应用启动时的配置选项。与 App.onLaunch 的回调参数一致。
## 返回值
Object
| 属性 | 类型 | 说明 |
| ------------ | ------ | --------------------------------------------------------------------------- |
| path | string | 启动快应用的路径 |
| query | object | 启动快应用的 query 参数 |
| referrerInfo | object | 来源信息。从另一个快应用、App 进入快应用时返回。否则返回 {}。(参见后文注意) |
### referrerInfo 的结构
| 属性 | 类型 | 说明 |
| --------- | ------ | ------------------------------------------------- |
| appId | string | 来源快应用、App 的 appId |
| extraData | object | 来源快应用传过来的数据,scene=1037 或 1038 时支持 |
::: warning 部分版本在无 referrerInfo 的时候会返回 undefined,建议使用 options.referrerInfo && options.referrerInfo.appId 进行判断。 :::
# qa.offAppHide(function callback)
取消监听快应用切后台事件
## 参数
### function callback
快应用切后台事件的回调函数
# qa.offAppShow(function callback)
取消监听快应用切前台事件
## 参数
### function callback
快应用切前台事件的回调函数
# qa.offError(function callback)
取消监听快应用错误事件。
## 参数
### function callback
快应用错误事件的回调函数
# qa.offPageNotFound(function callback)
取消监听快应用要打开的页面不存在事件
## 参数
### function callback
快应用要打开的页面不存在事件的回调函数
# qa.onAppHide(function callback)
监听快应用切后台事件。该事件与 [`App.onHide`](../../../../reference/api/App.html#onhide) 的回调时机一致。
## 参数
### function callback
快应用切后台事件的回调函数
# qa.onAppShow(function callback)
监听快应用切前台事件。该事件与 [`App.onShow`](../../../../reference/api/App.html#onshowobject-object) 的回调参数一致。
## 参数
### function callback
快应用切前台事件的回调函数
#### 参数
##### Object res
| 属性 | 类型 | 说明 |
| ----- | ------ | ------------------------- |
| path | string | 快应用切前台的路径 |
| query | Object | 快应用切前台的 query 参数 |
# qa.onError(function callback)
监听快应用错误事件。如脚本错误或 API 调用报错等。该事件与 [`App.onError`](../../../../reference/api/App.html#onerrorstring-error) 的回调时机与参数一致。
## 参数
### function callback
快应用错误事件的回调函数
#### 参数
##### string error
错误信息,包含堆栈
# console.debug()
向调试面板中打印 debug 日志
## 参数
### any ...args
日志内容,可以有任意多个。
# console.error()
向调试面板中打印 error 日志
## 参数
### any ...args
日志内容,可以有任意多个。
# console.group(string label)
在调试面板中创建一个新的分组。随后输出的内容都会被添加一个缩进,表示该内容属于当前分组。调用 [console.groupEnd](console.groupEnd.html)之后分组结束。
## 参数
### string label
分组标记,可选。
## 注意
仅在工具中有效,在 vConsole 中为空函数实现。
# console.groupEnd()
结束由 [console.group](console.group.html) 创建的分组
## 注意
仅在工具中有效,在 vConsole 中为空函数实现。
# console.info()
向调试面板中打印 info 日志
## 参数
### any ...args
日志内容,可以有任意多个。
# console.log()
向调试面板中打印 log 日志
## 参数
### any ...args
日志内容,可以有任意多个。
# console
向调试面板中打印日志。console 是一个全局对象,可以直接访问。在快应用中,向 vConsole 中输出日志。
## 方法
### [console.debug()](console.debug.html)
向调试面板中打印 debug 日志
### [console.log()](console.log.html)
向调试面板中打印 log 日志
### [console.info()](console.info.html)
向调试面板中打印 info 日志
### [console.warn()](console.warn.html)
向调试面板中打印 warn 日志
### [console.error()](console.error.html)
向调试面板中打印 error 日志
### [console.group(string label)](console.group.html)
在调试面板中创建一个新的分组。随后输出的内容都会被添加一个缩进,表示该内容属于当前分组。调用 [console.groupEnd](console.groupEnd.html)之后分组结束。
### [console.groupEnd()](console.groupEnd.html)
结束由 [console.group](console.group.html) 创建的分组
## 注意
::: warning
- 由于 vConsole 功能有限,以及不同客户端对 console 方法的支持情况有差异,建议开发者在快应用中只使用本文档中提供的方法。
# console.warn()
向调试面板中打印 warn 日志
## 参数
### any ...args
日志内容,可以有任意多个。
# qa.setEnableDebug(Object object)
设置是否打开调试开关。此开关对正式版也能生效。
## 参数
### Object object
| 属性 | 类型 | <div style="width: 50px">默认值</div> | <div style="width: 50px">必填</div> | 说明 |
| --- | --- | --- | --- | --- |
| enableDebug | boolean | | 是 | 是否打开调试 |
| success | function | | 否 | 接口调用成功的回调函数 |
| fail | function | | 否 | 接口调用失败的回调函数 |
| complete | function | | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
## 示例代码
```js
// 打开调试
qa.setEnableDebug({
enableDebug: true
})
// 关闭调试
qa.setEnableDebug({
enableDebug: false
})
```
## Tips
::: warning
- 在正式版打开调试还有一种方法,就是先在开发版或体验版打开调试,再切到正式版就能看到 vConsole。 :::
# string qa.arrayBufferToBase64(ArrayBuffer arrayBuffer)
将 ArrayBuffer 对象转成 Base64 字符串
## 参数
### ArrayBuffer arrayBuffer
要转换成 Base64 字符串的 ArrayBuffer 对象
## 返回值
### string
Base64 字符串
## 示例代码
```js
const arrayBuffer = new Uint8Array([11, 22, 33])
const base64 = qa.arrayBufferToBase64(arrayBuffer)
```
# ArrayBuffer qa.base64ToArrayBuffer(string base64)
将 Base64 字符串转成 ArrayBuffer 对象
## 参数
### string base64
要转化成 ArrayBuffer 对象的 Base64 字符串
## 返回值
### ArrayBuffer
ArrayBuffer 对象
## 示例代码
```js
const base64 = 'CxYh'
const arrayBuffer = qa.base64ToArrayBuffer(base64)
```
# boolean qa.canIUse(string schema)
判断指定API(含调用方式/回调/参数/返回值/返回值的属性)或者组件(含属性)在当前快应用版本是否可用。
## 参数
### string schema
对于API,支持下列调用Schema:
```
`${API}.${method}.${param}.${options}`
```
对于组件,支持下列调用Schema:
```
`${component}.${attribute}.${option}`
```
## 返回值
### boolean
指定API或者组件在当前快应用版本是否可用
## 参数说明
| Schema | 含义 |
| :------------: | :--------------------------------------------------------: |
| `${API}` | API 名字 |
| `${method}` | 调用方式,支持的有效值为 return, success, object, callback |
| `${param}` | 参数 or 返回值 |
| `${options}` | 参数的可选值 |
| `${component}` | 组件名字 |
| `${attribute}` | 组件属性 |
| `${option}` | 组件属性的可选值 |
## 示例代码
```js
qa.canIUse('openBluetoothAdapter')
qa.canIUse('getSystemInfoSync.return.screenWidth')
qa.canIUse('getSystemInfo.success.screenWidth')
qa.canIUse('showToast.object.image')
qa.canIUse('onCompassChange.callback.direction')
qa.canIUse('request.object.method.GET')
qa.canIUse('scroll-view')
qa.canIUse('text.selectable')
qa.canIUse('button.type.primary')
```
# qa.setTrustedUrls(Array array)
为组件 [web-view](/component/web-view.html) 所承载网页,配置可信任的网址(支持正则表达式)。
## 参数
### Array trustedUrlsArray
将认为可信任的网址,以数组的形式传入,届时该网页即可调用 [web-view](/component/web-view.html) 组件中所提供的 API 能力;对于数组中所配置的子项,可以是完整的网页地址,也可以是符合预期的正则表达式([web-view](/component/web-view.html) 的属性 `src` 所指定的链接,默认是可信任的,不必填入)。
## 返回值
无返回值。
## 示例代码
```js
// app.js
qa.setTrustedUrls([
'https://your-domain-name.cn/explore/',
/^https?:\/\/your-domain-name.com/,
/^https?:\/\/(www\.)your-domain-name.com/,
/^https?:\/\/([a-z0-9]*\.)your-domain-name.com/
])
```
# qa.getSystemInfo(Object object)
获取系统信息
## 参数
### Object object
| 属性 | 类型 | <div style="width: 50px">必填</div> | <div style="width: 50px">必填</div> | 说明 |
| --- | --- | --- | --- | --- |
| success | function | | 否 | 接口调用成功的回调函数 |
| fail | function | | 否 | 接口调用失败的回调函数 |
| complete | function | | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
#### object.success 回调函数
##### 参数
###### Object res
| 属性 | 类型 | 说明 |
| ---------------------- | ------- | --------------------------- |
| brand | string | 手机品牌 | |
| model | string | 手机型号 | |
| pixelRatio | number | 手机像素比 | |
| screenWidth | number | 手机屏幕宽度,单位 px | |
| screenHeight | number | 手机屏幕高度,单位 px | |
| windowWidth | number | 手机可使用窗口宽度,单位 px | |
| windowHeight | number | 手机可使用窗口高度,单位 px | |
| statusBarHeight | number | 手机状态栏的高度,单位 px | |
| language | string | 当前手机系统的语言 | |
| version | string | 快应用引擎版本号,字符串类型,比如"1054" | |
| SDKVersion | string | 快应用引擎版本号, number类型,比如1054 | |
| system | string | 手机操作系统及版本 | |
| platform | string | 手机平台,比如"android" | |
| fontSizeSetting | number | 用户设置字体大小(单位 px) | |
| cameraAuthorized | boolean | 当前快应用是否获得摄像头权限 | |
| locationAuthorized | boolean | 当前快应用是否获得定位权限 | |
| microphoneAuthorized | boolean | 当前快应用是否获得麦克风权限 | |
| bluetoothEnabled | boolean | 手机蓝牙的系统权限开关 | |
| locationEnabled | boolean | 手机地理位置的系统开关 | |
| wifiEnabled | boolean | 手机Wi-Fi的系统开关 | |
## 示例代码
```javascript
qa.getSystemInfo({
success(res) {
console.log(res.brand)
console.log(res.pixelRatio)
console.log(res.screenWidth)
console.log(res.screenHeight)
console.log(res.language)
console.log(res.statusBarHeight)
console.log(res.platform)
}
})
```
# Object qa.getSystemInfoSync()
[qa.getSystemInfo](qa.getSystemInfo.html) 的同步版本接口
## 返回值
### Object res
| 属性 | 类型 | 说明 |
| ---------------------- | ------- | --------------------------- |
| brand | string | 手机品牌 | |
| model | string | 手机型号 | |
| pixelRatio | number | 手机像素比 | |
| screenWidth | number | 手机屏幕宽度,单位 px | |
| screenHeight | number | 手机屏幕高度,单位 px | |
| windowWidth | number | 手机可使用窗口宽度,单位 px | |
| windowHeight | number | 手机可使用窗口高度,单位 px | |
| statusBarHeight | number | 手机状态栏的高度,单位 px | |
| language | string | 当前手机系统的语言 | |
| version | string | 快应用引擎版本号,字符串类型,比如"1054" | |
| SDKVersion | string | 快应用引擎版本号, number类型,比如1054 | |
| system | string | 手机操作系统及版本 | |
| platform | string | 手机平台,比如"android" | |
| fontSizeSetting | number | 用户设置字体大小(单位 px) | |
| cameraAuthorized | boolean | 当前快应用是否获得摄像头权限 | |
| locationAuthorized | boolean | 当前快应用是否获得定位权限 | |
| microphoneAuthorized | boolean | 当前快应用是否获得麦克风权限 | |
| bluetoothEnabled | boolean | 手机蓝牙的系统权限开关 | |
| locationEnabled | boolean | 手机地理位置的系统开关 | |
| wifiEnabled | boolean | 手机Wi-Fi的系统开关 | |
## 示例代码
```js
try {
const res = qa.getSystemInfoSync()
console.log(res.model)
console.log(res.pixelRatio)
console.log(res.windowWidth)
console.log(res.windowHeight)
console.log(res.language)
console.log(res.version)
console.log(res.platform)
} catch (e) {
// Do something when catch error
}
```
# clearInterval(number intervalID)
取消由 setInterval 设置的定时器。
## 参数
### number intervalID
要取消的定时器的 ID
# clearTimeout(number timeoutID)
取消由 setTimeout 设置的定时器。
## 参数
### number timeoutID
要取消的定时器的 ID
# number setInterval(function callback, number delay, any rest)
设定一个定时器。按照指定的周期(以毫秒计)来执行注册的回调函数
## 参数
### function callback
回调函数
### number delay
执行回调函数之间的时间间隔,单位 ms。
### any rest
param1, param2, ..., paramN 等附加参数,它们会作为参数传递给回调函数。
## 返回值
### number
定时器的编号。这个值可以传递给 [clearInterval](clearInterval.html) 来取消该定时。
# number setTimeout(function callback, number delay, any rest)
设定一个定时器。在定时到期以后执行注册的回调函数
## 参数
### function callback
回调函数
### number delay
延迟的时间,函数的调用会在该延迟之后发生,单位 ms。
### any rest
param1, param2, ..., paramN 等附加参数,它们会作为参数传递给回调函数。
## 返回值
### number
定时器的编号。这个值可以传递给 [clearTimeout](clearTimeout.html) 来取消该定时。
# CanvasContext.arc(number x, number y, number r, number sAngle, number eAngle, boolean counterclockwise)
创建一条弧线。
- 创建一个圆可以指定起始弧度为 0,终止弧度为 2 \* Math.PI。
-`stroke` 或者 `fill` 方法来在 `canvas` 中画弧线。
## 参数
### number x
圆心的 x 坐标
### number y
圆心的 y 坐标
### number r
圆的半径
### number sAngle
起始弧度,单位弧度(在 3 点钟方向)
### number eAngle
终止弧度
### boolean counterclockwise
弧度的方向是否是逆时针
## 示例代码
```js
const ctx = qa.createCanvasContext('myCanvas')
// Draw coordinates
ctx.arc(100, 75, 50, 0, 2 * Math.PI)
ctx.setFillStyle('#EEEEEE')
ctx.fill()
ctx.beginPath()
ctx.moveTo(40, 75)
ctx.lineTo(160, 75)
ctx.moveTo(100, 15)
ctx.lineTo(100, 135)
ctx.setStrokeStyle('#AAAAAA')
ctx.stroke()
ctx.setFontSize(12)
ctx.setFillStyle('black')
ctx.fillText('0', 165, 78)
ctx.fillText('0.5*PI', 83, 145)
ctx.fillText('1*PI', 15, 78)
ctx.fillText('1.5*PI', 83, 10)
// Draw points
ctx.beginPath()
ctx.arc(100, 75, 2, 0, 2 * Math.PI)
ctx.setFillStyle('lightgreen')
ctx.fill()
ctx.beginPath()
ctx.arc(100, 25, 2, 0, 2 * Math.PI)
ctx.setFillStyle('blue')
ctx.fill()
ctx.beginPath()
ctx.arc(150, 75, 2, 0, 2 * Math.PI)
ctx.setFillStyle('red')
ctx.fill()
// Draw arc
ctx.beginPath()
ctx.arc(100, 75, 50, 0, 1.5 * Math.PI)
ctx.setStrokeStyle('#333333')
ctx.stroke()
ctx.draw()
```
![](data:image/png;base64,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)
针对 arc(100, 75, 50, 0, 1.5 \* Math.PI)的三个关键坐标如下:
- 绿色: 圆心 (100, 75)
- 红色: 起始弧度 (0)
- 蓝色: 终止弧度 (1.5 \* Math.PI)
# CanvasContext.arcTo(number x1, number y1, number x2, number y2, number radius)
根据控制点和半径绘制圆弧路径。
## 参数
### number x1
第一个控制点的 x 轴坐标
### number y1
第一个控制点的 y 轴坐标
### number x2
第二个控制点的 x 轴坐标
### number y2
第二个控制点的 y 轴坐标
### number radius
圆弧的半径
# CanvasContext.beginPath()
开始创建一个路径。需要调用 `fill` 或者 `stroke` 才会使用路径进行填充或描边
- 在最开始的时候相当于调用了一次 `beginPath`
- 同一个路径内的多次 `setFillStyle``setStrokeStyle``setLineWidth`等设置,以最后一次设置为准。
## 示例代码
```js
const ctx = qa.createCanvasContext('myCanvas')
// begin path
ctx.rect(10, 10, 100, 30)
ctx.setFillStyle('yellow')
ctx.fill()
// begin another path
ctx.beginPath()
ctx.rect(10, 40, 100, 30)
// only fill this rect, not in current path
ctx.setFillStyle('blue')
ctx.fillRect(10, 70, 100, 30)
ctx.rect(10, 100, 100, 30)
// it will fill current path
ctx.setFillStyle('red')
ctx.fill()
ctx.draw()
```
![](data:image/png;base64,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)
# CanvasContext.bezierCurveTo()
创建三次方贝塞尔曲线路径。曲线的起始点为路径中前一个点。
## 示例代码
```js
const ctx = qa.createCanvasContext('myCanvas')
// Draw points
ctx.beginPath()
ctx.arc(20, 20, 2, 0, 2 * Math.PI)
ctx.setFillStyle('red')
ctx.fill()
ctx.beginPath()
ctx.arc(200, 20, 2, 0, 2 * Math.PI)
ctx.setFillStyle('lightgreen')
ctx.fill()
ctx.beginPath()
ctx.arc(20, 100, 2, 0, 2 * Math.PI)
ctx.arc(200, 100, 2, 0, 2 * Math.PI)
ctx.setFillStyle('blue')
ctx.fill()
ctx.setFillStyle('black')
ctx.setFontSize(12)
// Draw guides
ctx.beginPath()
ctx.moveTo(20, 20)
ctx.lineTo(20, 100)
ctx.lineTo(150, 75)
ctx.moveTo(200, 20)
ctx.lineTo(200, 100)