zoskit
Version:
Fast and lightweight UI component kit for ZeppOS
507 lines (449 loc) • 12.9 kB
JavaScript
import { getText } from "@zos/i18n";
import {
createWidget,
widget,
prop,
align,
text_style,
event,
deleteWidget,
} from "@zos/ui";
import { setStatusBarVisible } from "@zos/ui";
import {
getDeviceInfo,
SCREEN_SHAPE_SQUARE,
SCREEN_SHAPE_ROUND,
} from "@zos/device";
import { log, px } from "@zos/utils";
import { back } from "@zos/router";
import { onGesture, offGesture } from "@zos/interaction";
import { setScrollLock } from "@zos/page";
const {
width: DEVICE_WIDTH,
height: DEVICE_HEIGHT,
screenShape,
} = getDeviceInfo();
const PALETTE_POS_X = (DEVICE_WIDTH - px(260)) / 2;
const PALETTE_POS_Y =
screenShape === SCREEN_SHAPE_SQUARE
? (DEVICE_HEIGHT - px(260 + 60)) / 2
: (DEVICE_HEIGHT - px(260)) / 2;
setStatusBarVisible(false);
setScrollLock({ lock: true });
let h = 0; // angle
let s = 0; // radius
let v = 0; // lightness
let colorPreview;
let inputColor = 0xffffff;
//#region functions
function hex2rgb(hex) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return { r, g, b };
}
function toRadian(degree) {
return degree * (Math.PI / 180);
}
function toDegree(radian) {
return radian * (180 / Math.PI);
}
function lineLenght(dX, dY) {
return Math.sqrt(Math.pow(Math.abs(dX), 2) + Math.pow(Math.abs(dY), 2));
}
function angleDeg(dX, dY) {
let angle = Math.atan2(dY, dX);
let angleDeg = toDegree(angle);
if (angleDeg < 0) angleDeg += 360;
return angleDeg;
}
function setColor() {
//logger.log(`setColor`);
//logger.log(`hsv (${h}, ${s}, ${v})`);
let rgb = hsvToRgb(h, s, v);
let color = RGBToNumber(rgb[0], rgb[1], rgb[2]);
colorPreview.setProperty(prop.COLOR, color);
//logger.log(`rgb (${rgb[0]}, ${rgb[1]}, ${rgb[2]})`);
}
function RGBToHex(r, g, b) {
return (
"0x" +
r.toString(16).padStart(2, "0") +
g.toString(16).padStart(2, "0") +
b.toString(16).padStart(2, "0")
);
}
// https://gist.github.com/mjackson/5311256
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return Array The HSL representation
*/
function rgbToHsl(r, g, b) {
((r /= 255), (g /= 255), (b /= 255));
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h,
s,
l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [h, s, l];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number l The lightness
* @return Array The RGB representation
*/
function hslToRgb(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [r * 255, g * 255, b * 255];
}
/**
* Converts an RGB color value to HSV. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and v in the set [0, 1].
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return Array The HSV representation
*/
function rgbToHsv(r, g, b) {
((r /= 255), (g /= 255), (b /= 255));
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h,
s,
v = max;
var d = max - min;
s = max == 0 ? 0 : d / max;
if (max == min) {
h = 0; // achromatic
} else {
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [h, s, v];
}
/**
* Converts an HSV color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes h, s, and v are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number v The value
* @return Array The RGB representation
*/
function hsvToRgb(h, s, v) {
var r, g, b;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
((r = v), (g = t), (b = p));
break;
case 1:
((r = q), (g = v), (b = p));
break;
case 2:
((r = p), (g = v), (b = t));
break;
case 3:
((r = p), (g = q), (b = v));
break;
case 4:
((r = t), (g = p), (b = v));
break;
case 5:
((r = v), (g = p), (b = q));
break;
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
///////////////////////////
function RGBToNumber(r, g, b) {
return (r << 16) + (g << 8) + b;
}
//#endregion
class PaletteSelect {
constructor(x, y, color) {
this.widgets = [];
//logger.log(`PaletteSelect (${x}, ${y}, ${color})`);
let palette = createWidget(widget.IMG, {
x: x,
y: y,
src: "zoskit/palletesrc/Palette_r.png",
});
this.widgets.push(palette);
palette.addEventListener(event.CLICK_DOWN, function (info) {
//logger.log("CLICK_DOWN");
let posX = info.x - x;
let posY = info.y - y;
let dX = posX - px(260 / 2);
let dY = posY - px(260 / 2);
let angle = angleDeg(dX, dY);
h = angle / 360;
let lenght = lineLenght(dX, dY);
if (lenght > px(250 / 2)) lenght = px(250 / 2);
s = lenght / px(250 / 2);
if (pointer) {
pointer.setProperty(prop.MORE, {
pos_x: px((260 + s * 250) / 2),
angle: h * 360,
});
}
setColor();
let rgbNew = hsvToRgb(h, s, 1);
let colorNew = RGBToNumber(rgbNew[0], rgbNew[1], rgbNew[2]);
if (lightness) lightness.setProperty(prop.COLOR, colorNew);
});
palette.addEventListener(event.MOVE, function (info) {
let posX = info.x - x;
let posY = info.y - y;
let dX = posX - px(260 / 2);
let dY = posY - px(260 / 2);
let angle = angleDeg(dX, dY);
h = angle / 360;
let lenght = lineLenght(dX, dY);
if (lenght > px(250 / 2)) lenght = px(250 / 2);
s = lenght / px(250 / 2);
if (pointer) {
pointer.setProperty(prop.MORE, {
pos_x: px((260 + s * 250) / 2),
angle: h * 360,
});
}
setColor();
let rgbNew = hsvToRgb(h, s, 1);
let colorNew = RGBToNumber(rgbNew[0], rgbNew[1], rgbNew[2]);
if (lightness) lightness.setProperty(prop.COLOR, colorNew);
});
let colorString = "#" + color.toString(16).padStart(6, "0");
const rgb = hex2rgb(colorString);
const r = rgb.r;
const g = rgb.g;
const b = rgb.b;
//logger.log(`rgb (${r}, ${g}, ${b})`);
let hsv = rgbToHsv(r, g, b);
h = hsv[0]; // angle
s = hsv[1]; // radius
v = hsv[2]; // lightness
//logger.log(`hsv (${h}, ${s}, ${v})`);
let pointer = createWidget(widget.IMG, {
x: x - px(10),
y: y - px(10),
w: px(260 + 20),
h: px(260 + 20),
pos_x: px((260 + s * 250) / 2),
pos_y: px(260 / 2),
center_x: px((260 + 20) / 2),
center_y: px((260 + 20) / 2),
angle: h * 360,
src: "zoskit/palletesrc/Pointer_r.png",
});
this.widgets.push(pointer);
pointer.setEnable(false);
//////////////////
let lightness = createWidget(widget.FILL_RECT, {
x: x + px(5),
y: y + px(275),
w: px(250),
h: px(35),
color: color,
});
this.widgets.push(lightness);
let lightnessMask = createWidget(widget.IMG, {
x: x,
y: y + px(270),
src: "zoskit/palletesrc/LightnessMask.png",
});
this.widgets.push(lightnessMask);
let lightnessPointer = createWidget(widget.IMG, {
x: x + px(v * 250),
y: y + px(270 - 5),
src: "zoskit/palletesrc/Pointer.png",
});
this.widgets.push(lightnessPointer);
lightnessPointer.setEnable(false);
lightnessMask.addEventListener(event.CLICK_DOWN, function (info) {
//logger.log("CLICK_DOWN");
let posX = info.x - x + px(5);
let scale = posX / px(250);
if (scale > 1) scale = 1;
if (scale < 0) scale = 0;
//logger.log(`posX = ${posX}, scale = ${scale}`);
v = scale;
if (lightnessPointer) {
lightnessPointer.setProperty(prop.X, px(x + v * 250));
}
setColor();
});
lightnessMask.addEventListener(event.MOVE, function (info) {
// logger.log('MOVE');
let posX = info.x - x + px(5);
let scale = posX / px(250);
if (scale > 1) scale = 1;
if (scale < 0) scale = 0;
//logger.log(`posX = ${posX}, scale = ${scale}`);
v = scale;
if (lightnessPointer) {
lightnessPointer.setProperty(prop.X, x + px(v * 250));
}
setColor();
});
}
}
export function createColorPalette(visible = false) {
const widgets = [];
const fill = createWidget(widget.FILL_RECT, {
x: 0,
y: 0,
w: DEVICE_WIDTH,
h: DEVICE_HEIGHT,
radius: 0,
color: 0x000000,
});
widgets.push(fill);
colorPreview = createWidget(widget.FILL_RECT, {
x: (DEVICE_WIDTH - px(150)) / 2,
y: screenShape === SCREEN_SHAPE_SQUARE ? px(10) : px(30),
w: px(150),
h: px(50),
radius: 10,
color: 0x000000,
});
widgets.push(colorPreview);
const palette = new PaletteSelect(PALETTE_POS_X, PALETTE_POS_Y, inputColor);
widgets.push(...palette.widgets);
setColor();
let button_cancel = createWidget(widget.BUTTON, {
x:
screenShape === SCREEN_SHAPE_SQUARE
? DEVICE_WIDTH / 2 - px(210)
: px(-120),
y:
screenShape === SCREEN_SHAPE_SQUARE
? DEVICE_HEIGHT - px(60)
: (DEVICE_HEIGHT - px(90)) / 2,
w: -1,
h: -1,
normal_src: "zoskit/palletesrc/button_cancel.png",
press_src: "zoskit/palletesrc/button_cancel.png",
click_func: () => {
offGesture();
//logger.log("button_cancel");
widgets.forEach((w) => w.setProperty(prop.VISIBLE, false));
},
});
widgets.push(button_cancel);
let button_ok = createWidget(widget.BUTTON, {
x:
screenShape === SCREEN_SHAPE_SQUARE
? DEVICE_WIDTH / 2 + px(10)
: DEVICE_WIDTH - px(80),
y:
screenShape === SCREEN_SHAPE_SQUARE
? DEVICE_HEIGHT - px(60)
: (DEVICE_HEIGHT - px(90)) / 2,
w: -1,
h: -1,
normal_src: "zoskit/palletesrc/button_ok.png",
press_src: "zoskit/palletesrc/button_ok.png",
click_func: () => {
offGesture();
console.log("button_ok");
//widgets.forEach((w) => w.setProperty(prop.VISIBLE, false));
//logger.log(`hsv (${h}, ${s}, ${v})`);
let rgb = hsvToRgb(h, s, v);
let colorHex = RGBToHex(rgb[0], rgb[1], rgb[2]);
//logger.log(`rgb (${rgb[0]}, ${rgb[1]}, ${rgb[2]})`);
//console.log("COLOR", colorHex);
if (typeof api.onConfirm === "function") {
api.onConfirm(colorHex);
}
},
});
widgets.push(button_ok);
widgets.forEach((w) => w.setProperty(prop.VISIBLE, visible));
const api = {
widgets,
palette,
onConfirm: null, // сюда снаружи можно положить функцию
set visible(value) {
widgets.forEach((w) => w.setProperty(prop.VISIBLE, value));
if (value) {
onGesture({
callback: () => true,
});
} else {
offGesture();
}
},
get visible() {
return widgets.length ? widgets[0].getProperty(prop.VISIBLE) : false;
},
};
return api;
}