Merge pull request #1 from rain2o/feature/mix-blend-mode

add mix-blend-mode support
This commit is contained in:
kooriookami 2022-05-17 21:28:28 +08:00 committed by GitHub
commit fd76b44ef9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 164 additions and 1 deletions

View File

@ -46,6 +46,7 @@ Below is a list of all the supported CSS properties and values.
- max-width
- min-height
- min-width
- mix-blend-mode (**Limited support**)
- opacity
- overflow
- overflow-wrap
@ -79,7 +80,6 @@ These CSS properties are **NOT** currently supported
- [box-shadow](https://github.com/niklasvh/html2canvas/pull/1086)
- [filter](https://github.com/niklasvh/html2canvas/issues/493)
- [font-variant-ligatures](https://github.com/niklasvh/html2canvas/pull/1085)
- [mix-blend-mode](https://github.com/niklasvh/html2canvas/issues/580)
- [object-fit](https://github.com/niklasvh/html2canvas/issues/1064)
- [repeating-linear-gradient()](https://github.com/niklasvh/html2canvas/issues/1162)
- [writing-mode](https://github.com/niklasvh/html2canvas/issues/1258)

View File

@ -41,6 +41,7 @@ import {listStyleImage} from './property-descriptors/list-style-image';
import {listStylePosition} from './property-descriptors/list-style-position';
import {listStyleType} from './property-descriptors/list-style-type';
import {marginBottom, marginLeft, marginRight, marginTop} from './property-descriptors/margin';
import {mixBlendMode} from './property-descriptors/mix-blend-mode';
import {overflow, OVERFLOW} from './property-descriptors/overflow';
import {overflowWrap} from './property-descriptors/overflow-wrap';
import {paddingBottom, paddingLeft, paddingRight, paddingTop} from './property-descriptors/padding';
@ -126,6 +127,7 @@ export class CSSParsedDeclaration {
marginRight: CSSValue;
marginBottom: CSSValue;
marginLeft: CSSValue;
mixBlendMode: ReturnType<typeof mixBlendMode.parse>;
opacity: ReturnType<typeof opacity.parse>;
overflowX: OVERFLOW;
overflowY: OVERFLOW;
@ -194,6 +196,7 @@ export class CSSParsedDeclaration {
this.marginRight = parse(context, marginRight, declaration.marginRight);
this.marginBottom = parse(context, marginBottom, declaration.marginBottom);
this.marginLeft = parse(context, marginLeft, declaration.marginLeft);
this.mixBlendMode = parse(context, mixBlendMode, declaration.mixBlendMode);
this.opacity = parse(context, opacity, declaration.opacity);
const overflowTuple = parse(context, overflow, declaration.overflow);
this.overflowX = overflowTuple[0];

View File

@ -0,0 +1,77 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export const enum MIX_BLEND_MODE {
NORMAL = 'normal',
MULTIPLY = 'multiply',
SCREEN = 'screen',
OVERLAY = 'overlay',
DARKEN = 'darken',
LIGHTEN = 'lighten',
COLOR_DODGE = 'color-dodge',
COLOR_BURN = 'color-burn',
HARD_LIGHT = 'hard-light',
SOFT_LIGHT = 'soft-light',
DIFFERENCE = 'difference',
EXCLUSION = 'exclusion',
HUE = 'hue',
SATURATION = 'saturation',
COLOR = 'color',
LUMINOSITY = 'luminosity',
INITIAL = 'initial',
INHERIT = 'inherit',
REVERT = 'revert',
UNSET = 'unset'
}
export const mixBlendMode: IPropertyIdentValueDescriptor<MIX_BLEND_MODE> = {
name: 'mix-blend-mode',
initialValue: 'normal',
prefix: false,
type: PropertyDescriptorParsingType.IDENT_VALUE,
parse: (_context: Context, mode: string) => {
switch (mode) {
case 'multiply':
return MIX_BLEND_MODE.MULTIPLY;
case 'screen':
return MIX_BLEND_MODE.SCREEN;
case 'overlay':
return MIX_BLEND_MODE.OVERLAY;
case 'darken':
return MIX_BLEND_MODE.DARKEN;
case 'lighten':
return MIX_BLEND_MODE.LIGHTEN;
case 'color-dodge':
return MIX_BLEND_MODE.COLOR_DODGE;
case 'color-burn':
return MIX_BLEND_MODE.COLOR_BURN;
case 'hard-light':
return MIX_BLEND_MODE.HARD_LIGHT;
case 'soft-light':
return MIX_BLEND_MODE.SOFT_LIGHT;
case 'difference':
return MIX_BLEND_MODE.DIFFERENCE;
case 'exclusion':
return MIX_BLEND_MODE.EXCLUSION;
case 'hue':
return MIX_BLEND_MODE.HUE;
case 'saturation':
return MIX_BLEND_MODE.SATURATION;
case 'color':
return MIX_BLEND_MODE.COLOR;
case 'luminosity':
return MIX_BLEND_MODE.LUMINOSITY;
case 'initial':
return MIX_BLEND_MODE.INITIAL;
case 'inherit':
return MIX_BLEND_MODE.INHERIT;
case 'revert':
return MIX_BLEND_MODE.REVERT;
case 'unset':
return MIX_BLEND_MODE.UNSET;
case 'normal':
default:
return MIX_BLEND_MODE.NORMAL;
}
}
};

1
src/global.d.ts vendored
View File

@ -2,6 +2,7 @@ interface CSSStyleDeclaration {
textDecorationColor: string;
textDecorationLine: string;
overflowWrap: string;
mixBlendMode: string;
}
interface DocumentType extends Node, ChildNode {

View File

@ -189,6 +189,7 @@ export class CanvasRenderer extends Renderer {
switch (paintOrderLayer) {
case PAINT_ORDER_LAYER.FILL:
this.ctx.fillStyle = asString(styles.color);
this.ctx.globalCompositeOperation = styles.mixBlendMode;
this.renderTextWithLetterSpacing(text, styles.letterSpacing, baseline);
const textShadows: TextShadow = styles.textShadow;
@ -276,6 +277,7 @@ export class CanvasRenderer extends Renderer {
this.path(path);
this.ctx.save();
this.ctx.clip();
this.ctx.globalCompositeOperation = container.styles.mixBlendMode;
this.ctx.drawImage(
image,
0,
@ -714,6 +716,7 @@ export class CanvasRenderer extends Renderer {
if (!isTransparent(styles.backgroundColor)) {
this.ctx.fillStyle = asString(styles.backgroundColor);
this.ctx.globalCompositeOperation = styles.mixBlendMode;
this.ctx.fill();
}

View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<title>Mix Blend Mode tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript" src="../test.js"></script>
<style>
.circle {
width: 80px;
height: 80px;
border-radius: 50%;
position: absolute;
}
.circle-1 {
background: red;
}
.circle-2 {
background: green;
left: 40px;
}
.circle-3 {
background: skyblue;
left: 20px;
top: 40px;
}
.container {
position: relative;
border: 1px solid purple;
margin-bottom: 5px;
}
.bg-image {
position: relative;
background-image: url('../assets/image.jpg');
width: 120px;
height: 120px;
}
</style>
</head>
<body>
<div class="container">
<p>Green circle: mix-blend-mode: screen</p>
<p>Blue circle: mix-blend-mode: color-burn</p>
<div style="height: 120px; position: relative; background-color: gray;">
<div class="circle circle-1"></div>
<div class="circle circle-2" style="mix-blend-mode: screen"></div>
<div class="circle circle-3" style="mix-blend-mode: color-burn"></div>
</div>
</div>
<div class="container">
<p>Red background with mix-blend-mode: overlay over a background image</p>
<div class="bg-image">
<div
style="
position: absolute;
height: 100%;
width: 100%;
background-color: red;
mix-blend-mode: overlay;
"
></div>
</div>
</div>
<div class="container" style="background-color: green">
<p>Image with mix-blend-mode: multiply</p>
<img src="../assets/image2.jpg" style="mix-blend-mode: multiply" />
</div>
<div class="container" style="height: 120px; background-color: white;">
<div class="circle" style="position: relative; display: inline-block; background-color: red"></div>
<div class="circle" style="position: relative; display: inline-block; background-color: blue"></div>
<div class="circle" style="position: relative; display: inline-block; background-color: green"></div>
<div class="circle" style="position: relative; display: inline-block; background-color: yellow"></div>
<div class="circle" style="position: relative; display: inline-block; background-color: darkblue"></div>
<div class="circle" style="position: relative; display: inline-block; background-image: url('../assets/image2_1.jpg')"></div>
<p style="mix-blend-mode: color; font-weight: 900; font-size: x-large; position: absolute; top: 0;">
mix-blend-mode: color; Large text over background colors.
</p>
</div>
</body>
</html>