@nativescript-community/ui-neumorphiclayout
Version:
Neumorphism-based layout for NativeScript
219 lines • 9.76 kB
JavaScript
import { Color, CssProperty, LayoutBase, Length, PercentLength, Style, Utils } from '@nativescript/core';
import { Canvas, createRectF, Direction, Paint, Path, Style as drawStyle, TileMode, LinearGradient } from '@nativescript-community/ui-canvas';
import { LinearGradient as NSLinearGradient } from '@nativescript/core/ui/styling/linear-gradient';
const stylePropertiesMap = new Map();
const defaultColor = '#ffffff';
export var NeumorphicType;
(function (NeumorphicType) {
NeumorphicType["FLAT"] = "flat";
NeumorphicType["PRESSED"] = "pressed";
NeumorphicType["PRESSED_IN_OUT"] = "pressed-in-out";
})(NeumorphicType || (NeumorphicType = {}));
stylePropertiesMap.set('lightShadowColor', new CssProperty({
name: 'lightShadowColor',
cssName: 'light-shadow-color',
defaultValue: new Color(defaultColor),
equalityComparer: Color.equals,
valueConverter: (value) => new Color(value),
}));
export const lightShadowColorProperty = stylePropertiesMap.get('lightShadowColor');
stylePropertiesMap.set('darkShadowColor', new CssProperty({
name: 'darkShadowColor',
cssName: 'dark-shadow-color',
defaultValue: new Color('#d9d9d9'),
equalityComparer: Color.equals,
valueConverter: (value) => new Color(value),
}));
export const darkShadowColorProperty = stylePropertiesMap.get('darkShadowColor');
stylePropertiesMap.set('neumorphism', new CssProperty({
name: 'neumorphism',
cssName: 'neumorphism',
defaultValue: null,
}));
export const neumorphismProperty = stylePropertiesMap.get('neumorphism');
stylePropertiesMap.set('shadowDistance', new CssProperty({
name: 'shadowDistance',
cssName: 'shadow-distance',
defaultValue: 10,
valueConverter: parseFloat,
}));
export const shadowDistanceProperty = stylePropertiesMap.get('shadowDistance');
stylePropertiesMap.set('shadowRadius', new CssProperty({
name: 'shadowRadius',
cssName: 'shadow-radius',
valueConverter: parseFloat,
}));
export const shadowRadiusProperty = stylePropertiesMap.get('shadowRadius');
export class NeumorphicCanvas extends Canvas {
constructor(view) {
super(0, 0);
this.mViewRef = new WeakRef(view);
this.paintBase = new Paint();
this.paintBase.setAntiAlias(true);
this.paintDark = new Paint();
this.paintDark.setAntiAlias(true);
this.paintLight = new Paint();
this.paintLight.setAntiAlias(true);
}
manipulateColor(color, factor) {
const a = color.a;
const r = Math.round(color.r * factor);
const g = Math.round(color.g * factor);
const b = Math.round(color.b * factor);
return new Color(a, Math.min(r, 255), Math.min(g, 255), Math.min(b, 255));
}
getBasePaint() {
return this.paintBase;
}
onDraw() {
const view = this.mViewRef && this.mViewRef.get();
const state = view.neumorphism;
if (!state) {
return;
}
// Stroke paint will be set at a later stage if needed
this.paintStroke = null;
if (state == NeumorphicType.PRESSED_IN_OUT) {
if (__ANDROID__) {
this.initShape(NeumorphicType.FLAT);
this.initPaints(NeumorphicType.FLAT);
this.drawPath(this.path, this.paintDark);
this.drawPath(this.path, this.paintLight);
this.drawPath(this.path, this.paintBase);
}
this.initShape(NeumorphicType.PRESSED);
this.initPaints(NeumorphicType.PRESSED);
this.clipPath(this.path);
this.drawPath(this.path, this.paintBase);
this.drawPath(this.innerShadowPath, this.paintDark);
this.drawPath(this.innerShadowPath, this.paintLight);
}
else {
this.initShape(state);
this.initPaints(state);
if (state == NeumorphicType.PRESSED) {
this.clipPath(this.path);
this.drawPath(this.path, this.paintBase);
this.drawPath(this.innerShadowPath, this.paintDark);
this.drawPath(this.innerShadowPath, this.paintLight);
}
else {
if (__ANDROID__) {
this.drawPath(this.path, this.paintDark);
this.drawPath(this.path, this.paintLight);
}
this.drawPath(this.path, this.paintBase);
}
}
if (this.paintStroke != null) {
this.clipPath(this.path);
this.drawPath(this.path, this.paintStroke);
}
}
initPaints(state) {
const view = this.mViewRef && this.mViewRef.get();
const shadowRadius = view.shadowRadius || view.shadowDistance * 2;
const isPressable = state == NeumorphicType.PRESSED || state == NeumorphicType.PRESSED_IN_OUT;
const fillColor = view.style.backgroundColor instanceof Color ? view.style.backgroundColor : defaultColor;
this._setBackground(view);
this._setStroke(view);
if (isPressable) {
this.paintDark.strokeWidth = shadowRadius;
this.paintDark.style = drawStyle.STROKE;
this.paintLight.strokeWidth = shadowRadius;
this.paintLight.style = drawStyle.STROKE;
}
else {
this.paintDark.strokeWidth = 0;
this.paintDark.style = drawStyle.FILL;
this.paintLight.strokeWidth = 0;
this.paintLight.style = drawStyle.FILL;
}
this.paintDark.setColor(fillColor);
this.paintLight.setColor(fillColor);
this.paintDark.setShadowLayer(shadowRadius, view.shadowDistance, view.shadowDistance, view.darkShadowColor);
this.paintLight.setShadowLayer(shadowRadius, -view.shadowDistance, -view.shadowDistance, view.lightShadowColor);
}
initShape(state) {
const view = this.mViewRef && this.mViewRef.get();
const { width, height } = view.getActualSize();
const cornerRadiusDip = Utils.layout.toDeviceIndependentPixels(Length.toDevicePixels(view.borderTopLeftRadius));
const cornerRadius = Math.min(Math.min(width, height) / 2, cornerRadiusDip);
const shadowRadius = view.shadowRadius || view.shadowDistance * 2;
this.path = new Path();
this.innerShadowPath = new Path();
this.path.addRoundRect(createRectF(0, 0, width, height), cornerRadius, cornerRadius, Direction.CW);
this.innerShadowPath.addRoundRect(createRectF(-(shadowRadius / 2), -(shadowRadius / 2), width + shadowRadius, height + shadowRadius), cornerRadius, cornerRadius, Direction.CW);
}
_setBackground(view) {
const background = view.style.backgroundInternal;
if (!background) {
return;
}
if (background.image) {
if (background.image instanceof NSLinearGradient) {
const gradient = background.image;
const { width, height } = view.getActualSize();
const colors = [];
const positions = new Float32Array(gradient.colorStops.length);
let isGradientWithStops = false;
for (let i = 0, length = gradient.colorStops.length; i < length; i++) {
const colorStop = gradient.colorStops[i];
colors[i] = colorStop.color;
if (colorStop.offset) {
isGradientWithStops = true;
positions[i] = Utils.layout.toDeviceIndependentPixels(PercentLength.toDevicePixels(colorStop.offset));
}
}
const alpha = gradient.angle / (Math.PI * 2);
const startX = Math.pow(Math.sin(Math.PI * (alpha + 0.75)), 2);
const startY = Math.pow(Math.sin(Math.PI * (alpha + 0.5)), 2);
const endX = Math.pow(Math.sin(Math.PI * (alpha + 0.25)), 2);
const endY = Math.pow(Math.sin(Math.PI * alpha), 2);
this.paintBase.setShader(new LinearGradient(startX * width, startY * height, endX * width, endY * height, colors, isGradientWithStops ? positions : null, TileMode.MIRROR));
}
else {
throw new Error('Neumorphism plugin can only draw color and linear gradient backgrounds');
}
}
else {
if (this.paintBase.getShader()) {
this.paintBase.setShader(null);
}
this.paintBase.setColor(view.style.backgroundColor || defaultColor);
}
}
_setStroke(view) {
const borderColor = view.style.borderTopColor instanceof Color ? view.style.borderTopColor : defaultColor;
// Duplicate width as half of it will be clipped
const borderWidth = Utils.layout.toDeviceIndependentPixels(Length.toDevicePixels(view.style.borderTopWidth)) * 2;
if (borderWidth > 0) {
this.paintStroke = new Paint();
this.paintStroke.setStyle(drawStyle.STROKE);
this.paintStroke.setAntiAlias(true);
this.paintStroke.setStrokeWidth(borderWidth);
this.paintStroke.setColor(borderColor);
}
}
}
const disposeNativeViewOrig = LayoutBase.prototype.disposeNativeView;
LayoutBase.prototype.disposeNativeView = function () {
disposeNativeViewOrig.call(this);
if (this.augmentedCanvas != null) {
delete this.augmentedCanvas;
}
};
// Style properties
for (let [key, value] of stylePropertiesMap) {
Object.defineProperty(LayoutBase.prototype, key, {
get() {
return this.style[key];
},
set(value) {
this.style[key] = value;
},
enumerable: true,
});
value.register(Style);
}
//# sourceMappingURL=common.js.map