mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-20 12:38:50 +00:00
Merge branch 'v2-codecs' into jxl
This commit is contained in:
@@ -3,21 +3,23 @@ import { h, Component } from 'preact';
|
||||
import * as style from './style.scss';
|
||||
import { bind } from '../../lib/initial-util';
|
||||
import { cleanSet, cleanMerge } from '../../lib/clean-modify';
|
||||
import OptiPNGEncoderOptions from '../../codecs/optipng/options';
|
||||
import OxiPNGEncoderOptions from '../../codecs/oxipng/options';
|
||||
import MozJpegEncoderOptions from '../../codecs/mozjpeg/options';
|
||||
import BrowserJPEGEncoderOptions from '../../codecs/browser-jpeg/options';
|
||||
import WebPEncoderOptions from '../../codecs/webp/options';
|
||||
import JXLEncoderOptions from '../../codecs/jxl/options';
|
||||
import AvifEncoderOptions from '../../codecs/avif/options';
|
||||
import BrowserWebPEncoderOptions from '../../codecs/browser-webp/options';
|
||||
|
||||
import QuantizerOptionsComponent from '../../codecs/imagequant/options';
|
||||
import ResizeOptionsComponent from '../../codecs/resize/options';
|
||||
|
||||
import * as identity from '../../codecs/identity/encoder-meta';
|
||||
import * as optiPNG from '../../codecs/optipng/encoder-meta';
|
||||
import * as oxiPNG from '../../codecs/oxipng/encoder-meta';
|
||||
import * as mozJPEG from '../../codecs/mozjpeg/encoder-meta';
|
||||
import * as webP from '../../codecs/webp/encoder-meta';
|
||||
import * as jxl from '../../codecs/jxl/encoder-meta';
|
||||
import * as avif from '../../codecs/avif/encoder-meta';
|
||||
import * as browserPNG from '../../codecs/browser-png/encoder-meta';
|
||||
import * as browserJPEG from '../../codecs/browser-jpeg/encoder-meta';
|
||||
import * as browserWebP from '../../codecs/browser-webp/encoder-meta';
|
||||
@@ -46,10 +48,11 @@ const encoderOptionsComponentMap: {
|
||||
[x: string]: (new (...args: any[]) => Component<any, any>) | undefined;
|
||||
} = {
|
||||
[identity.type]: undefined,
|
||||
[optiPNG.type]: OptiPNGEncoderOptions,
|
||||
[oxiPNG.type]: OxiPNGEncoderOptions,
|
||||
[mozJPEG.type]: MozJpegEncoderOptions,
|
||||
[webP.type]: WebPEncoderOptions,
|
||||
[jxl.type]: JXLEncoderOptions,
|
||||
[avif.type]: AvifEncoderOptions,
|
||||
[browserPNG.type]: undefined,
|
||||
[browserJPEG.type]: BrowserJPEGEncoderOptions,
|
||||
[browserWebP.type]: BrowserWebPEncoderOptions,
|
||||
|
||||
@@ -7,10 +7,11 @@ import Output from '../Output';
|
||||
import Options from '../Options';
|
||||
import ResultCache from './result-cache';
|
||||
import * as identity from '../../codecs/identity/encoder-meta';
|
||||
import * as optiPNG from '../../codecs/optipng/encoder-meta';
|
||||
import * as oxiPNG from '../../codecs/oxipng/encoder-meta';
|
||||
import * as mozJPEG from '../../codecs/mozjpeg/encoder-meta';
|
||||
import * as webP from '../../codecs/webp/encoder-meta';
|
||||
import * as jxl from '../../codecs/jxl/encoder-meta';
|
||||
import * as avif from '../../codecs/avif/encoder-meta';
|
||||
import * as browserPNG from '../../codecs/browser-png/encoder-meta';
|
||||
import * as browserJPEG from '../../codecs/browser-jpeg/encoder-meta';
|
||||
import * as browserWebP from '../../codecs/browser-webp/encoder-meta';
|
||||
@@ -110,11 +111,6 @@ async function preprocessImage(
|
||||
} else if (isHqx(preprocessData.resize)) {
|
||||
// Hqx can only do x2, x3 or x4.
|
||||
result = await processor.workerResize(result, preprocessData.resize);
|
||||
// Seems like the globals from Rust from hqx and resize are conflicting.
|
||||
// For now we can fix that by terminating the worker.
|
||||
// TODO: Use wasm-bindgen’s new --web target to create a proper ES6 module
|
||||
// and remove this.
|
||||
processor.terminateWorker();
|
||||
// If the target size is not a clean x2, x3 or x4, use Catmull-Rom
|
||||
// for the remaining scaling.
|
||||
const pixelOpts = { ...preprocessData.resize, method: 'catrom' };
|
||||
@@ -139,10 +135,11 @@ async function compressImage(
|
||||
): Promise<Fileish> {
|
||||
const compressedData = await (() => {
|
||||
switch (encodeData.type) {
|
||||
case optiPNG.type: return processor.optiPngEncode(image, encodeData.options);
|
||||
case oxiPNG.type: return processor.oxiPngEncode(image, encodeData.options);
|
||||
case mozJPEG.type: return processor.mozjpegEncode(image, encodeData.options);
|
||||
case webP.type: return processor.webpEncode(image, encodeData.options);
|
||||
case jxl.type: return processor.jxlEncode(image, encodeData.options);
|
||||
case avif.type: return processor.avifEncode(image, encodeData.options);
|
||||
case browserPNG.type: return processor.browserPngEncode(image);
|
||||
case browserJPEG.type: return processor.browserJpegEncode(image, encodeData.options);
|
||||
case browserWebP.type: return processor.browserWebpEncode(image, encodeData.options);
|
||||
|
||||
@@ -41,17 +41,31 @@ const demos = [
|
||||
},
|
||||
];
|
||||
|
||||
const installButtonSource = 'introInstallButton-Purple';
|
||||
|
||||
interface Props {
|
||||
onFile: (file: File | Fileish) => void;
|
||||
showSnack: SnackBarElement['showSnackbar'];
|
||||
}
|
||||
interface State {
|
||||
fetchingDemoIndex?: number;
|
||||
beforeInstallEvent?: BeforeInstallPromptEvent;
|
||||
}
|
||||
|
||||
export default class Intro extends Component<Props, State> {
|
||||
state: State = {};
|
||||
private fileInput?: HTMLInputElement;
|
||||
private installingViaButton = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Listen for beforeinstallprompt events, indicating Squoosh is installable.
|
||||
window.addEventListener('beforeinstallprompt', this.onBeforeInstallPromptEvent);
|
||||
|
||||
// Listen for the appinstalled event, indicating Squoosh has been installed.
|
||||
window.addEventListener('appinstalled', this.onAppInstalled);
|
||||
}
|
||||
|
||||
@bind
|
||||
private resetFileInput() {
|
||||
@@ -90,7 +104,71 @@ export default class Intro extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
render({ }: Props, { fetchingDemoIndex }: State) {
|
||||
@bind
|
||||
private onBeforeInstallPromptEvent(event: BeforeInstallPromptEvent) {
|
||||
// Don't show the mini-infobar on mobile
|
||||
event.preventDefault();
|
||||
|
||||
// Save the beforeinstallprompt event so it can be called later.
|
||||
this.setState({ beforeInstallEvent: event });
|
||||
|
||||
// Log the event.
|
||||
const gaEventInfo = {
|
||||
eventCategory: 'pwa-install',
|
||||
eventAction: 'promo-shown',
|
||||
nonInteraction: true,
|
||||
};
|
||||
ga('send', 'event', gaEventInfo);
|
||||
}
|
||||
|
||||
@bind
|
||||
private async onInstallClick(event: Event) {
|
||||
// Get the deferred beforeinstallprompt event
|
||||
const beforeInstallEvent = this.state.beforeInstallEvent;
|
||||
// If there's no deferred prompt, bail.
|
||||
if (!beforeInstallEvent) return;
|
||||
|
||||
this.installingViaButton = true;
|
||||
|
||||
// Show the browser install prompt
|
||||
beforeInstallEvent.prompt();
|
||||
|
||||
// Wait for the user to accept or dismiss the install prompt
|
||||
const { outcome } = await beforeInstallEvent.userChoice;
|
||||
// Send the analytics data
|
||||
const gaEventInfo = {
|
||||
eventCategory: 'pwa-install',
|
||||
eventAction: 'promo-clicked',
|
||||
eventLabel: installButtonSource,
|
||||
eventValue: outcome === 'accepted' ? 1 : 0,
|
||||
};
|
||||
ga('send', 'event', gaEventInfo);
|
||||
|
||||
// If the prompt was dismissed, we aren't going to install via the button.
|
||||
if (outcome === 'dismissed') {
|
||||
this.installingViaButton = false;
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
private onAppInstalled() {
|
||||
// We don't need the install button, if it's shown
|
||||
this.setState({ beforeInstallEvent: undefined });
|
||||
|
||||
// Don't log analytics if page is not visible
|
||||
if (document.hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to get the install, if it's not set, use 'browser'
|
||||
const source = this.installingViaButton ? installButtonSource : 'browser';
|
||||
ga('send', 'event', 'pwa-install', 'installed', source);
|
||||
|
||||
// Clear the install method property
|
||||
this.installingViaButton = false;
|
||||
}
|
||||
|
||||
render({ }: Props, { fetchingDemoIndex, beforeInstallEvent }: State) {
|
||||
return (
|
||||
<div class={style.intro}>
|
||||
<div>
|
||||
@@ -120,7 +198,7 @@ export default class Intro extends Component<Props, State> {
|
||||
<img class={style.demoIcon} src={demo.iconUrl} alt="" decoding="async" />
|
||||
{fetchingDemoIndex === i &&
|
||||
<div class={style.demoLoading}>
|
||||
<loading-spinner class={style.demoLoadingSpinner}/>
|
||||
<loading-spinner class={style.demoLoadingSpinner} />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -132,11 +210,20 @@ export default class Intro extends Component<Props, State> {
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
{beforeInstallEvent &&
|
||||
<button
|
||||
type="button"
|
||||
class={style.installButton}
|
||||
onClick={this.onInstallClick}
|
||||
>
|
||||
Install
|
||||
</button>
|
||||
}
|
||||
<ul class={style.relatedLinks}>
|
||||
<li><a href="https://github.com/GoogleChromeLabs/squoosh/">View the code</a></li>
|
||||
<li><a href="https://github.com/GoogleChromeLabs/squoosh/issues">Report a bug</a></li>
|
||||
<li>
|
||||
<a href="https://github.com/GoogleChromeLabs/squoosh/blob/master/README.md#privacy">
|
||||
<a href="https://github.com/GoogleChromeLabs/squoosh/blob/dev/README.md#privacy">
|
||||
Privacy
|
||||
</a>
|
||||
</li>
|
||||
|
||||
32
src/components/intro/missing-types.d.ts
vendored
Normal file
32
src/components/intro/missing-types.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* The BeforeInstallPromptEvent is fired at the Window.onbeforeinstallprompt handler
|
||||
* before a user is prompted to "install" a web site to a home screen on mobile.
|
||||
*/
|
||||
interface BeforeInstallPromptEvent extends Event {
|
||||
|
||||
/**
|
||||
* Returns an array of DOMString items containing the platforms on which the event was dispatched.
|
||||
* This is provided for user agents that want to present a choice of versions to the user such as,
|
||||
* for example, "web" or "play" which would allow the user to chose between a web version or
|
||||
* an Android version.
|
||||
*/
|
||||
readonly platforms: Array<string>;
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves to a DOMString containing either "accepted" or "dismissed".
|
||||
*/
|
||||
readonly userChoice: Promise<{
|
||||
outcome: 'accepted' | 'dismissed',
|
||||
platform: string
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Allows a developer to show the install prompt at a time of their own choosing.
|
||||
* This method returns a Promise.
|
||||
*/
|
||||
prompt(): Promise<void>;
|
||||
}
|
||||
|
||||
interface WindowEventMap {
|
||||
"beforeinstallprompt": BeforeInstallPromptEvent;
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
overscroll-behavior: contain;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
@@ -170,6 +171,31 @@
|
||||
--color: #fff;
|
||||
}
|
||||
|
||||
.install-button {
|
||||
composes: unbutton from '../../lib/util.scss';
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: #504488;
|
||||
}
|
||||
|
||||
background: #5D509E;
|
||||
border: 1px solid #e8e8e8;
|
||||
color: #fff;
|
||||
padding: 14px;
|
||||
font-size: 1.3rem;
|
||||
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
|
||||
animation: fade-in .3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
}
|
||||
|
||||
.related-links {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
|
||||
@@ -12,8 +12,8 @@ export default class Select extends Component<Props, State> {
|
||||
|
||||
return (
|
||||
<div class={style.select}>
|
||||
<select class={`${style.nativeSelect} ${large ? style.large : ''}`} {...otherProps}/>
|
||||
<svg class={style.arrow} viewBox="0 0 10 5"><path d="M0 0l5 5 5-5z"/></svg>
|
||||
<select class={`${style.builtinSelect} ${large ? style.large : ''}`} {...otherProps} />
|
||||
<svg class={style.arrow} viewBox="0 0 10 5"><path d="M0 0l5 5 5-5z" /></svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.native-select {
|
||||
.builtin-select {
|
||||
background: #2f2f2f;
|
||||
border-radius: 4px;
|
||||
font: inherit;
|
||||
|
||||
Reference in New Issue
Block a user