Backdrop filter compatibility for Firefox and Safari
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import React, { createRef } from "react";
|
||||
import "context-filter-polyfill";
|
||||
|
||||
interface IProps {
|
||||
width?: number;
|
||||
height?: number;
|
||||
backgroundImage?: ImageBitmap;
|
||||
backgroundImage?: CanvasImageSource;
|
||||
blur?: string;
|
||||
}
|
||||
|
||||
@@ -31,20 +32,28 @@ export default class BackdropPanel extends React.PureComponent<IProps> {
|
||||
this.canvasRef.current.width = width;
|
||||
this.canvasRef.current.height = height;
|
||||
|
||||
const destinationX = width - backgroundImage.width;
|
||||
const destinationY = height - backgroundImage.height;
|
||||
const imageWidth = (backgroundImage as ImageBitmap).width
|
||||
|| (backgroundImage as HTMLImageElement).naturalWidth;
|
||||
const imageHeight = (backgroundImage as ImageBitmap).height
|
||||
|| (backgroundImage as HTMLImageElement).naturalHeight;
|
||||
|
||||
const destinationX = width - imageWidth;
|
||||
const destinationY = height - imageHeight;
|
||||
|
||||
this.ctx.filter = `blur(${this.props.blur})`;
|
||||
this.ctx.drawImage(
|
||||
backgroundImage,
|
||||
Math.min(destinationX, 0),
|
||||
Math.min(destinationY, 0),
|
||||
Math.max(width, backgroundImage.width),
|
||||
Math.max(height, backgroundImage.height),
|
||||
Math.max(width, imageWidth),
|
||||
Math.max(height, imageHeight),
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <canvas ref={this.canvasRef} className="mx_BackdropPanel" />;
|
||||
return <canvas
|
||||
ref={this.canvasRef}
|
||||
className="mx_BackdropPanel"
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ import BackdropPanel from "./BackdropPanel";
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
backgroundImage?: ImageBitmap;
|
||||
backgroundImage?: CanvasImageSource;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
||||
@@ -124,7 +124,7 @@ interface IState {
|
||||
usageLimitEventTs?: number;
|
||||
useCompactLayout: boolean;
|
||||
activeCalls: Array<MatrixCall>;
|
||||
backgroundImage?: ImageBitmap;
|
||||
backgroundImage?: CanvasImageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -181,7 +181,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCo
|
||||
});
|
||||
|
||||
interface IProps {
|
||||
backgroundImage?: ImageBitmap;
|
||||
backgroundImage?: CanvasImageSource;
|
||||
}
|
||||
|
||||
const SpacePanel = (props: IProps) => {
|
||||
|
||||
@@ -19,11 +19,12 @@ import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { User } from "matrix-js-sdk/src/models/user";
|
||||
import { throttle } from "lodash";
|
||||
import { memoize, throttle } from "lodash";
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import { _t } from "../languageHandler";
|
||||
import {mediaFromMxc} from "../customisations/Media";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import { getDrawable } from "../utils/drawable";
|
||||
|
||||
interface IState {
|
||||
displayName?: string;
|
||||
@@ -138,7 +139,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
await this.updateState({displayName: profileInfo.displayname, avatarUrl: profileInfo.avatar_url});
|
||||
};
|
||||
|
||||
public async getAvatarBitmap(avatarSize = 32): Promise<ImageBitmap> {
|
||||
public async getAvatarBitmap(avatarSize = 32): Promise<CanvasImageSource> {
|
||||
let avatarUrl = this.getHttpAvatarUrl(avatarSize);
|
||||
const settingBgMxc = SettingsStore.getValue("RoomList.backgroundImage");
|
||||
if (settingBgMxc) {
|
||||
@@ -146,14 +147,14 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
if (avatarUrl) {
|
||||
const response = await fetch(avatarUrl);
|
||||
const blob = await response.blob();
|
||||
return await createImageBitmap(blob);
|
||||
return await this.buildBitmap(avatarUrl);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private buildBitmap = memoize(getDrawable);
|
||||
|
||||
private onStateEvents = throttle(async (ev: MatrixEvent) => {
|
||||
const myUserId = MatrixClientPeg.get().getUserId();
|
||||
if (ev.getType() === 'm.room.member' && ev.getSender() === myUserId && ev.getStateKey() === myUserId) {
|
||||
|
||||
24
src/utils/drawable.ts
Normal file
24
src/utils/drawable.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Fetch an image using the best available method based on browser compatibility
|
||||
* @param url the URL of the image to fetch
|
||||
* @returns a canvas drawable object
|
||||
*/
|
||||
export async function getDrawable(url: string): Promise<CanvasImageSource> {
|
||||
if ('createImageBitmap' in window) {
|
||||
const response = await fetch(url);
|
||||
const blob = await response.blob();
|
||||
return await createImageBitmap(blob);
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = document.createElement("img");
|
||||
img.crossOrigin = "anonymous";
|
||||
img.onload = function() {
|
||||
resolve(img);
|
||||
}
|
||||
img.onerror = function(e) {
|
||||
reject(e);
|
||||
}
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user