Commit 111a20da authored by David Reveman's avatar David Reveman Committed by Commit Bot

Change Picture: Improve photo taking animations.

This improves the flash animation for photo taking by removing
the use of a mask-image in favor of a border that can be animated
and doesn't conflict with flash animations. This makes the removal
of the circular frame used for preview less jarring.

It also adjusts the delay of animations and defers image processing
so that flash animations have a chance to finish running. Single
photo mode uses white color ease-in animation, while video mode
uses a less intense flash with 0.67 opacity.

Last, it uses the first frame of PNGs as placeholder while
larger animated PNGs are loading from object URLs. This prevents
the loading from appearing as a flash.

Bug: 773987
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: Ibd1a2ec06984a38df685d6f5e29963debcc91190
Reviewed-on: https://chromium-review.googlesource.com/742801
Commit-Queue: David Reveman <reveman@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#512612}
parent 6e0b7ba5
......@@ -21,6 +21,7 @@
{
'dependencies': [
'cr_camera',
'cr_png_behavior',
'cr_picture_types',
],
'target_name': 'cr_picture_pane',
......
......@@ -10,6 +10,11 @@
<!-- Note: Do not include cr-shared-style, we use different
paper-icon-button-light styling -->
<style>
:host {
--cr-camera-hidden-frame-size: 406px;
--cr-camera-preview-frame-size: 280px;
}
#perspectiveBox {
height: var(--cr-camera-image-size, 228px);
margin: auto;
......@@ -28,29 +33,41 @@
}
#userImageStreamCrop::after {
-webkit-mask-image: radial-gradient(transparent 140px, black 140px);
background-color: rgb(214, 214, 214);
border: 100px solid rgba(214, 214, 214, 0.34);
border-radius: 100%;
content: '';
display: block;
height: 100%;
opacity: 0.34;
transform: translateZ(0);
width: 100%;
height: var(--cr-camera-hidden-frame-size);
margin: 50% 50%;
transform: translateZ(0) translateX(-50%) translateY(-50%);
transition: width 100ms, height 100ms;
transition-timing-function: ease-in;
width: var(--cr-camera-hidden-frame-size);
}
#userImageStreamCrop.preview::after {
height: var(--cr-camera-preview-frame-size);
transition-timing-function: ease-out;
width: var(--cr-camera-preview-frame-size);
}
@-webkit-keyframes pulse {
0% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0; }
:host([!videomode]) #userImageStreamCrop.capture::after {
background-color: white;
transition: background-color 50ms ease-in 100ms;
}
#userImageStreamCrop.capture::after {
-webkit-mask-image: none;
animation: pulse linear 100ms normal forwards;
@-webkit-keyframes flash {
0% { background-color: rgba(255, 255, 255, 0); }
33% { background-color: rgba(255, 255, 255, 0.67); }
100% { background-color: rgba(255, 255, 255, 0); }
}
:host([videomode]) #userImageStreamCrop.capture::after {
animation: pulse linear 50ms 20;
animation-delay: 100ms;
animation-duration: 100ms;
animation-iteration-count: 10;
animation-name: flash;
animation-timing-function: ease-out;
}
paper-spinner {
......
......@@ -61,6 +61,7 @@ Polymer({
/** @override */
attached: function() {
this.$.cameraVideo.addEventListener('canplay', function() {
this.$.userImageStreamCrop.classList.add('preview');
this.cameraOnline_ = true;
}.bind(this));
this.startCamera();
......@@ -97,17 +98,19 @@ Polymer({
/** Start capturing frames at an interval. */
var capturedFrames = [];
this.$.userImageStreamCrop.classList.remove('preview');
this.$.userImageStreamCrop.classList.add('capture');
var interval = setInterval(() => {
capturedFrames.push(this.captureFrame_(this.$.cameraVideo, frames.pop()));
/** Stop capturing frames when all allocated frames have been consumed. */
if (!frames.length) {
this.$.userImageStreamCrop.classList.remove('capture');
if (frames.length) {
capturedFrames.push(
this.captureFrame_(this.$.cameraVideo, frames.pop()));
} else {
clearInterval(interval);
this.fire(
'photo-taken',
{photoDataUrl: this.convertFramesToPng_(capturedFrames)});
this.$.userImageStreamCrop.classList.remove('capture');
}
}, CAPTURE_INTERVAL_MS);
},
......@@ -142,6 +145,7 @@ Polymer({
/** Stops the camera stream capture if it's currently active. */
stopCamera: function() {
this.$.userImageStreamCrop.classList.remove('preview');
this.cameraOnline_ = false;
this.$.cameraVideo.src = '';
if (this.cameraStream_)
......
......@@ -4,6 +4,7 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
<link rel="import" href="cr_camera.html">
<link rel="import" href="cr_picture_types.html">
<link rel="import" href="cr_png_behavior.html">
<dom-module id="cr-picture-pane">
<template>
......@@ -21,6 +22,7 @@
}
img {
background-size: 100% 100%;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
display: block;
......@@ -61,19 +63,16 @@
border-radius: 50%;
}
</style>
<template is="dom-if"
if="[[showImagePreview_(cameraActive_, imageSrc)]]">
<div id="preview">
<img alt="[[previewAltText]]" src="[[getImgSrc_(imageUrl)]]"
data-show-discard$="[[showDiscard_(imageType)]]">
<div id="discard" hidden="[[!showDiscard_(imageType)]]">
<button is="paper-icon-button-light" id="discardImage"
class="icon-delete-white" title="[[discardImageLabel]]"
on-tap="onTapDiscardImage_">
</button>
</div>
<div id="preview" hidden="[[!showImagePreview_(cameraActive_, imageSrc)]]">
<img id="image" alt="[[previewAltText]]" src="[[getImgSrc_(imageUrl)]]"
data-show-discard$="[[showDiscard_(imageType)]]">
<div id="discard" hidden="[[!showDiscard_(imageType)]]">
<button is="paper-icon-button-light" id="discardImage"
class="icon-delete-white" title="[[discardImageLabel]]"
on-tap="onTapDiscardImage_">
</button>
</div>
</template>
</div>
<template is="dom-if" if="[[cameraActive_]]">
<cr-camera id="camera"
take-photo-label="[[takePhotoLabel]]"
......
......@@ -11,7 +11,10 @@
Polymer({
is: 'cr-picture-pane',
behaviors: [CrPngBehavior],
properties: {
/** Whether the camera is present / available */
cameraPresent: Boolean,
......@@ -93,14 +96,19 @@ Polymer({
* Data URLs for PNG images can be large. Create an object URL to avoid
* URL length limits.
*/
var image = /** @type {!HTMLImageElement} */ (this.$$('#image'));
if (this.imageSrc.startsWith('data:image/png')) {
var byteString = atob(this.imageSrc.split(',')[1]);
var bytes = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++)
bytes[i] = byteString.charCodeAt(i);
var blob = new Blob([bytes], {'type': 'image/png'});
// Use first frame as placeholder while rest of image loads.
image.style.backgroundImage = 'url(' +
CrPngBehavior.convertImageSequenceToPng([this.imageSrc]) + ')';
this.imageUrl = URL.createObjectURL(blob);
} else {
image.style.backgroundImage = 'none';
this.imageUrl = this.imageSrc;
}
},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment