Merge pull request #266 from marcosmoura/components/mdImage
Components/md image
|
|
@ -75,6 +75,10 @@
|
|||
<router-link exact to="/components/icon">Icon</router-link>
|
||||
</md-list-item>
|
||||
|
||||
<md-list-item class="md-inset">
|
||||
<router-link exact to="/components/image-loader">Image Loader</router-link>
|
||||
</md-list-item>
|
||||
|
||||
<md-list-item class="md-inset">
|
||||
<router-link exact to="/components/input">Input</router-link>
|
||||
</md-list-item>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
docs/src/assets/joker-1.jpg
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
docs/src/assets/joker-2.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
docs/src/assets/joker-3.jpg
Normal file
|
After Width: | Height: | Size: 114 KiB |
103
docs/src/pages/components/ImageLoader.vue
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<template>
|
||||
<page-content page-title="Components - Image Loader">
|
||||
<docs-component>
|
||||
<div slot="description">
|
||||
<p>Illustrations and photographs may load and transition in three phases by staggering opacity, exposure, and saturation levels.</p>
|
||||
</div>
|
||||
|
||||
<div slot="api">
|
||||
<api-table name="md-image">
|
||||
<md-table slot="properties">
|
||||
<md-table-header>
|
||||
<md-table-row>
|
||||
<md-table-head>Name</md-table-head>
|
||||
<md-table-head>Type</md-table-head>
|
||||
<md-table-head>Description</md-table-head>
|
||||
</md-table-row>
|
||||
</md-table-header>
|
||||
|
||||
<md-table-body>
|
||||
<md-table-row>
|
||||
<md-table-cell>md-src</md-table-cell>
|
||||
<md-table-cell><code>String</code></md-table-cell>
|
||||
<md-table-cell>The image source. Accepts any image file extension.</md-table-cell>
|
||||
</md-table-row>
|
||||
</md-table-body>
|
||||
</md-table>
|
||||
</api-table>
|
||||
</div>
|
||||
|
||||
<div slot="example">
|
||||
<example-box card-title="Default">
|
||||
<div slot="demo">
|
||||
<md-button class="md-primary md-raised" @click="loadImage">Load Image</md-button>
|
||||
<md-button class="md-primary md-raised" @click="clearImage">Clear Image</md-button>
|
||||
|
||||
<div>
|
||||
<md-image :md-src="src"></md-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div slot="code">
|
||||
<code-block lang="xml">
|
||||
<md-button class="md-primary md-raised" @click="loadImage">Load Image</md-button>
|
||||
<md-button class="md-primary md-raised" @click="clearImage">Clear Image</md-button>
|
||||
|
||||
<div>
|
||||
<md-image :md-src="src"></md-image>
|
||||
</div>
|
||||
</code-block>
|
||||
|
||||
<code-block lang="xml">
|
||||
export default {
|
||||
data: () => ({
|
||||
src: null
|
||||
}),
|
||||
methods: {
|
||||
loadImage() {
|
||||
let options = [
|
||||
'assets/joker-1.jpg',
|
||||
'assets/joker-2.jpg',
|
||||
'assets/joker-3.jpg',
|
||||
'assets/card-image-1.jpg',
|
||||
'assets/card-image-2.jpg'
|
||||
];
|
||||
|
||||
this.src = options[Math.floor(Math.random() * 5)];
|
||||
},
|
||||
clearImage() {
|
||||
this.src = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
</code-block>
|
||||
</div>
|
||||
</example-box>
|
||||
</div>
|
||||
</docs-component>
|
||||
</page-content>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: () => ({
|
||||
src: null
|
||||
}),
|
||||
methods: {
|
||||
loadImage() {
|
||||
let options = [
|
||||
'assets/joker-1.jpg',
|
||||
'assets/joker-2.jpg',
|
||||
'assets/joker-3.jpg',
|
||||
'assets/card-image-1.jpg',
|
||||
'assets/card-image-2.jpg'
|
||||
];
|
||||
|
||||
this.src = options[Math.floor(Math.random() * 5)];
|
||||
},
|
||||
clearImage() {
|
||||
this.src = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -15,6 +15,7 @@ const Checkbox = (resolve) => require(['./pages/components/Checkbox'], resolve);
|
|||
const Chips = (resolve) => require(['./pages/components/Chips'], resolve);
|
||||
const Dialog = (resolve) => require(['./pages/components/Dialog'], resolve);
|
||||
const Icon = (resolve) => require(['./pages/components/Icon'], resolve);
|
||||
const ImageLoader = (resolve) => require(['./pages/components/ImageLoader'], resolve);
|
||||
const InkRipple = (resolve) => require(['./pages/components/InkRipple'], resolve);
|
||||
const Input = (resolve) => require(['./pages/components/Input'], resolve);
|
||||
const List = (resolve) => require(['./pages/components/List'], resolve);
|
||||
|
|
@ -114,6 +115,11 @@ const components = [
|
|||
name: 'components:icon',
|
||||
component: Icon
|
||||
},
|
||||
{
|
||||
path: '/components/image-loader',
|
||||
name: 'components:image-loader',
|
||||
component: ImageLoader
|
||||
},
|
||||
{
|
||||
path: '/components/ink-ripple',
|
||||
name: 'components:ink-ripple',
|
||||
|
|
|
|||
|
|
@ -6,40 +6,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
const getImageAlpha = (image, onLoad) => {
|
||||
let canvas = document.createElement('canvas');
|
||||
|
||||
image.onload = function() {
|
||||
let colorSum = 0;
|
||||
let ctx;
|
||||
let imageData;
|
||||
let imageMetadata;
|
||||
let r;
|
||||
let g;
|
||||
let b;
|
||||
let average;
|
||||
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.drawImage(this, 0, 0);
|
||||
|
||||
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
imageMetadata = imageData.data;
|
||||
|
||||
for (let x = 0, len = imageMetadata.length; x < len; x += 4) {
|
||||
r = imageMetadata[x];
|
||||
g = imageMetadata[x + 1];
|
||||
b = imageMetadata[x + 2];
|
||||
|
||||
average = Math.floor((r + g + b) / 3);
|
||||
colorSum += average;
|
||||
}
|
||||
|
||||
onLoad(Math.floor(colorSum / (this.width * this.height)));
|
||||
};
|
||||
};
|
||||
import getImageLightness from '../../core/utils/getImageLightness';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -82,7 +49,7 @@
|
|||
let image = this.$el.querySelector('img');
|
||||
|
||||
if (image && (this.mdTextScrim || this.mdSolid)) {
|
||||
getImageAlpha(image, (lightness) => {
|
||||
getImageLightness(image, (lightness) => {
|
||||
let limit = 256;
|
||||
let darkness = (Math.abs(limit - lightness) * 100 / limit + 15) / 100;
|
||||
|
||||
|
|
|
|||
8
src/components/mdImage/index.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import mdImage from './mdImage.vue';
|
||||
import mdImageTheme from './mdImage.theme';
|
||||
|
||||
export default function install(Vue) {
|
||||
Vue.component('md-image', Vue.extend(mdImage));
|
||||
|
||||
Vue.material.styles.push(mdImageTheme);
|
||||
}
|
||||
19
src/components/mdImage/mdImage.scss
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$transition-duration: 2.2s;
|
||||
|
||||
.md-image {
|
||||
opacity: 0;
|
||||
filter: saturate(20%);
|
||||
|
||||
&.md-black-output {
|
||||
filter: brightness(.4) saturate(20%);
|
||||
}
|
||||
|
||||
&.md-loaded {
|
||||
opacity: 1;
|
||||
filter: saturate(100%);
|
||||
transition: opacity $transition-duration/2 $swift-ease-out-timing-function,
|
||||
filter $transition-duration .3s $swift-ease-out-timing-function;
|
||||
}
|
||||
}
|
||||
5
src/components/mdImage/mdImage.theme
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.THEME_NAME {
|
||||
&.md-image {
|
||||
|
||||
}
|
||||
}
|
||||
64
src/components/mdImage/mdImage.vue
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<img :src="mdSrc" class="md-image" :class="classes">
|
||||
</template>
|
||||
|
||||
<style lang="scss" src="./mdImage.scss"></style>
|
||||
|
||||
<script>
|
||||
import getImageLightness from '../../core/utils/getImageLightness';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
mdSrc: String
|
||||
},
|
||||
data: () => ({
|
||||
loaded: false,
|
||||
applyBlack: true,
|
||||
imageElement: null
|
||||
}),
|
||||
watch: {
|
||||
mdSrc() {
|
||||
this.createImage();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes() {
|
||||
return {
|
||||
'md-loaded': this.loaded,
|
||||
'md-black-output': this.applyBlack
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
analyzeLightness(image) {
|
||||
getImageLightness(image, (lightness) => {
|
||||
let limit = 256;
|
||||
let darkness = (Math.abs(limit - lightness) * 100 / limit + 15) / 100;
|
||||
|
||||
if (darkness >= 0.7) {
|
||||
this.applyBlack = true;
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
});
|
||||
},
|
||||
createImage() {
|
||||
this.loaded = false;
|
||||
this.applyBlack = false;
|
||||
this.imageElement = null;
|
||||
|
||||
if (this.mdSrc) {
|
||||
this.imageElement = document.createElement('img');
|
||||
this.imageElement.crossOrigin = '';
|
||||
this.imageElement.src = this.mdSrc;
|
||||
this.analyzeLightness(this.imageElement);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.createImage();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -134,10 +134,11 @@
|
|||
return idList.indexOf(id);
|
||||
},
|
||||
calculateIndicatorPos() {
|
||||
if (this.$refs.tabHeader) {
|
||||
let activeTab = this.$refs.tabHeader && this.$refs.tabHeader[this.activeTabNumber];
|
||||
|
||||
if (activeTab) {
|
||||
let tabsWidth = this.$el.offsetWidth;
|
||||
let activeTab = this.$refs.tabHeader[this.activeTabNumber];
|
||||
let left = activeTab ? activeTab.offsetLeft : 0;
|
||||
let left = activeTab.offsetLeft;
|
||||
let right = tabsWidth - left - activeTab.offsetWidth;
|
||||
|
||||
this.$refs.indicator.style.left = left + 'px';
|
||||
|
|
|
|||
|
|
@ -44,9 +44,12 @@ canvas,
|
|||
video,
|
||||
iframe {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
font-style: italic;
|
||||
vertical-align: middle;
|
||||
|
||||
&:not(.md-image) {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
36
src/core/utils/getImageLightness.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
const getImageLightness = (image, onLoad) => {
|
||||
let canvas = document.createElement('canvas');
|
||||
|
||||
image.onload = function() {
|
||||
let colorSum = 0;
|
||||
let ctx;
|
||||
let imageData;
|
||||
let imageMetadata;
|
||||
let r;
|
||||
let g;
|
||||
let b;
|
||||
let average;
|
||||
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.drawImage(this, 0, 0);
|
||||
|
||||
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
imageMetadata = imageData.data;
|
||||
|
||||
for (let x = 0, len = imageMetadata.length; x < len; x += 4) {
|
||||
r = imageMetadata[x];
|
||||
g = imageMetadata[x + 1];
|
||||
b = imageMetadata[x + 2];
|
||||
|
||||
average = Math.floor((r + g + b) / 3);
|
||||
colorSum += average;
|
||||
}
|
||||
|
||||
onLoad(Math.floor(colorSum / (this.width * this.height)));
|
||||
};
|
||||
};
|
||||
|
||||
export default getImageLightness;
|
||||
|
|
@ -10,6 +10,7 @@ import MdChips from './components/mdChips';
|
|||
import MdDialog from './components/mdDialog';
|
||||
import MdDivider from './components/mdDivider';
|
||||
import MdIcon from './components/mdIcon';
|
||||
import MdImage from './components/mdImage';
|
||||
import MdInputContainer from './components/mdInputContainer';
|
||||
import MdLayout from './components/mdLayout';
|
||||
import MdList from './components/mdList';
|
||||
|
|
@ -40,6 +41,7 @@ const options = {
|
|||
MdDialog,
|
||||
MdDivider,
|
||||
MdIcon,
|
||||
MdImage,
|
||||
MdInputContainer,
|
||||
MdLayout,
|
||||
MdList,
|
||||
|
|
|
|||