Skip to content
Permalink
Browse files
feature(b-icon): add animated icon options (closes #4720) (#4934)
Co-authored-by: Jacob Müller
  • Loading branch information
tmorehouse committed Mar 12, 2020
1 parent c15f86a commit 7c781faea78315a753b2db903b12c500d6547ae1
Showing 16 changed files with 1,752 additions and 11 deletions.
@@ -276,7 +276,7 @@ module.exports = {
'highlight.js/styles/atom-one-light.css',
'codemirror/lib/codemirror.css',
'bootstrap/dist/css/bootstrap.css',
'../scripts/build.scss', // BootstrapVue SCSS
'../scripts/index.scss', // BootstrapVue SCSS
'@assets/css/docs.min.css',
'@assets/scss/styles.scss'
]
@@ -96,16 +96,16 @@ node-sass --output-style expanded \
--source-map true \
--source-map-contents true \
--precision 6 \
scripts/build.scss \
scripts/index.scss \
dist/bootstrap-vue.css
postcss --config scripts/postcss.config.js \
--replace dist/bootstrap-vue.css
# Icons only CSS
# BootstrapVue Icons only CSS
node-sass --output-style expanded \
--source-map true \
--source-map-contents true \
--precision 6 \
src/icons.scss \
scripts/icons.scss \
dist/bootstrap-vue-icons.css
postcss --config scripts/postcss.config.js \
--replace dist/bootstrap-vue-icons.css
@@ -0,0 +1,11 @@
/*!
* BootstrapVue Icons Custom CSS (https://bootstrap-vue.js.org)
*/

// Include Bootstrap functions, variables, and mixins
@import "node_modules/bootstrap/scss/functions";
@import "node_modules/bootstrap/scss/variables";
@import "node_modules/bootstrap/scss/mixins";

// Import BootstrapVue Icons custom SCSS
@import "../src/icons.scss";
File renamed without changes.
@@ -59,6 +59,13 @@ $b-custom-file-height-inner-sm: calc(
#{$b-custom-file-line-height-sm * 1em} + #{$b-custom-file-padding-y-sm * 2}
) !default;

// --- Icons ---

// Animations
$b-icon-animation-spin-duration: 2s !default;
$b-icon-animation-pulse-duration: 1s !default;
$b-icon-animation-cylon-duration: 0.75s !default;

// --- Tables ---

// Table busy state
@@ -222,4 +222,8 @@ label is provided. You can easily customize the role if required via prop `role`
As well, when no label is provided, the spinner will automatically have the attribute
`aria-hidden="true"` to hide the spinner from screen reader users.

## See also

An alternative to the `<b-spinner>` component are [animated icons](/docs/icons/#animated-icons).

<!-- Component reference added automatically from component package.json -->
@@ -1,5 +1,8 @@
// --- BootstrapVue Icons Custom SCSS ---

// Include variables and utilities first
@import "variables";
@import "utilities";

// Include custom SCSS for icons
// Temporary until Bootstrap v5 (maybe)
@import "icons/index";
@@ -297,7 +297,7 @@ With the use of Bootstrap's border and background
<!-- icons-styling.vue -->
```

## Transforms
## SVG transforms

BootstrapVue icons provide several props for applying basic SVG transforms to the `<svg>`. All
transforms can be combined for added effect. Note that the transforms are applied to the `<svg>`
@@ -428,6 +428,64 @@ Shifting is applied after any rotation transforms. As with scaling, backgrounds
affected. If you need to shift the border/background with the icon, use Bootstrap's margin
[spacing utility classes](/docs/reference/utility-classes).

## Animated icons

<span class="badge badge-info small">v2.7.0+</span>

BootstrapVue includes two spinning animation options for icons: `spin` and `pulse`. Both animations
spin the icon clockwise, but pulse uses a stepped spin. A third animation called `cylon` is also
provided.

To use the spin animation, set the `animation` prop to one of the animation names `'spin'`,
`'pulse'` or `'cylon'`.

```html
<template>
<div>
<p>Spinning animation:</p>
<b-icon icon="arrow-clockwise" animation="spin" font-scale="4"></b-icon>

<p class="mt-3">Pulsing animation:</p>
<b-icon icon="arrow-clockwise" animation="pulse" font-scale="4"></b-icon>

<p class="mt-3">Cylon animation:</p>
<b-icon icon="three-dots" animation="cylon" font-scale="4"></b-icon>
</div>
</template>

<!-- b-icon-spin-aminations.vue -->
```

Note with the `cylon` animation, the left-right movement extends past the icon's bounding box by
`25%`, so you may need to adjust padding or margins to compensate for your use case.

As the animations are CSS based, they are applied _after_ any SVG transforms have taken place:

```html
<template>
<div class="p-4">
<b-icon icon="clock" animation="spin" font-scale="4" shift-v="8"></b-icon>
</div>
</template>

<!-- b-icon-spin-aminations-transforms.vue -->
```

The BootstrapVue defined icon animation effects require BootstrapVue's custom CSS. The `animation`
prop translates to the class name `b-icon-animation-{animationName}`.

Need a different style animation? Just create a custom class defining the animation, and apply that
class to the icon component, or create a new animation class in the form of
`b-icon-animation-{animationName}` and pass the custom animation name to the `animation` prop.

**Note:** The BootstrapVue defined animation effects of this component is dependent on the
`prefers-reduced-motion` media query. See the
[reduced motion section of our accessibility documentation](/docs/reference/accessibility) for
additional details.

Side note: the `cylon` animation gets its name from the "eye" of the Cylons from the _original_
[1978 Battlestar Galactica TV series](https://www.youtube.com/watch?v=5a5bEIf0UaU).

## Stacking icons

<span class="badge badge-info small">v2.3.0+</span>
@@ -492,6 +550,8 @@ Stacked icon notes:
- The `font-scale` prop cannot be used on the inner icon components
- The `width` and `height` attributes cannot be applied to the inner icon components
- Stacked icons **cannot** be stacked inside another `<b-iconstack>`
- Note the animation props on the child icons will have no effect, however you _can_ use the
animation props on the `<b-iconstack>` component.

## Using in components

@@ -9,6 +9,47 @@
// Perhaps this values should be SASS variables?
vertical-align: -0.15em;
}

&.b-icon-animation-spin {
animation: $b-icon-animation-spin-duration infinite linear b-icon-animation-spin;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}

&.b-icon-animation-pulse {
animation: $b-icon-animation-pulse-duration infinite steps(8) b-icon-animation-spin;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}

&.b-icon-animation-cylon {
animation: $b-icon-animation-cylon-duration infinite ease-in-out alternate
b-icon-animation-cylon;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
}

// Animation for spinning icons
@keyframes b-icon-animation-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
}

@keyframes b-icon-animation-cylon {
0% {
transform: translateX(-25%);
}
100% {
transform: translateX(25%);
}
}

// Make icons slightly larger in buttons, nav-links, dropdowns, and input-group-text
@@ -37,6 +37,10 @@ export const commonIconProps = {
shiftV: {
type: [Number, String],
default: 0
},
animation: {
type: String,
default: null
}
}

@@ -73,6 +77,7 @@ export const BVIconBase = /*#__PURE__*/ Vue.extend({
const shiftV = toFloat(props.shiftV) || 0
const flipH = props.flipH
const flipV = props.flipV
const animation = props.animation
// Compute the transforms
// Note that order is important as SVG transforms are applied in order from
// left to right and we want flipping/scale to occur before rotation
@@ -116,7 +121,10 @@ export const BVIconBase = /*#__PURE__*/ Vue.extend({
mergeData(
{
staticClass: 'b-icon bi',
class: { [`text-${props.variant}`]: !!props.variant },
class: {
[`text-${props.variant}`]: !!props.variant,
[`b-icon-animation-${animation}`]: !!animation
},
attrs: baseAttrs,
style: isStacked ? {} : { fontSize: fontScale === 1 ? null : `${fontScale * 100}%` }
},
@@ -1,7 +1,7 @@
// --- BEGIN AUTO-GENERATED FILE ---
//
// @IconsVersion: 1.0.0-alpha2
// @Generated: 2020-01-22T07:06:51.693Z
// @Generated: 2020-03-12T09:18:47.956Z
//
// This file is generated on each build. Do not edit this file!

@@ -1,7 +1,7 @@
// --- BEGIN AUTO-GENERATED FILE ---
//
// @IconsVersion: 1.0.0-alpha2
// @Generated: 2020-01-22T07:06:51.693Z
// @Generated: 2020-03-12T09:18:47.956Z
//
// This file is generated on each build. Do not edit this file!

@@ -430,4 +430,22 @@ describe('icons', () => {
)
expect(wrapper.find('svg > g > g > path').exists()).toBe(true)
})

it('b-icon animation prop works', async () => {
const wrapper = mount(BIcon, {
localVue: localVue,
parentComponent: parentComponent,
propsData: {
icon: 'circle-fill',
animation: 'spin'
}
})

expect(wrapper.exists()).toBe(true)
expect(wrapper.is('svg')).toBe(true)
expect(wrapper.classes()).toContain('b-icon')
expect(wrapper.classes()).toContain('bi')
expect(wrapper.classes()).toContain('bi-circle-fill')
expect(wrapper.classes()).toContain('b-icon-animation-spin')
})
})

0 comments on commit 7c781fa

Please sign in to comment.