Toggle navigation
Toggle navigation
This project
Loading...
Sign in
developOne
/
harmonyPool
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
chenjun3_wd
2024-04-25 18:11:47 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
fead8804303ef7d375060382965454c62de216f0
fead8804
1 parent
273f68e8
早晚报背景根据图片变换
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
454 additions
and
1 deletions
sight_harmony/features/wdComponent/src/main/ets/components/MorningEveningPaper/MorningEveningPaperComponent.ets
sight_harmony/features/wdComponent/src/main/ets/utils/ColorUtils.ets
sight_harmony/features/wdComponent/src/main/ets/utils/ImageUtils.ets
sight_harmony/features/wdComponent/src/main/ets/components/MorningEveningPaper/MorningEveningPaperComponent.ets
View file @
fead880
...
...
@@ -9,6 +9,10 @@ import { SingleColumn999Component } from './SingleColumn999Component';
import { topicInfoView } from './topicInfoView';
import { DateFormatUtil, PlayerConstants, WDPlayerController } from 'wdPlayer';
import { AudioDataList } from 'wdBean/src/main/ets/bean/morningevening/AudioDataList';
import { image } from '@kit.ImageKit';
import { getPicture, imageNet2PixelMap } from '../../utils/ImageUtils';
import { effectKit } from '@kit.ArkGraphics2D';
import { window } from '@kit.ArkUI';
const TAG = 'MorningEveningPaperComponent';
...
...
@@ -47,6 +51,9 @@ export struct MorningEveningPaperComponent {
@Provide currentTime: string = "00:00";
@Provide totalTime: string = "00:00";
@Provide progressVal: number = 0;
@State mixedBgColor: string = ''
// 顶部安全高度赋值
@State topSafeHeight: number = 0;
private audioDataList: AudioDataList[] = []
private playerController: WDPlayerController = new WDPlayerController();
simpleAudioDialog: CustomDialogController = new CustomDialogController({
...
...
@@ -93,6 +100,10 @@ export struct MorningEveningPaperComponent {
}
async aboutToAppear() {
let windowHight: window.Window = await window.getLastWindow(getContext(this));
await windowHight.setWindowLayoutFullScreen(true);
this.topSafeHeight = px2vp(windowHight.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height)
let dailyPaperTopicPageId = await SPHelper.default.getSync('dailyPaperTopicPageId', "") as String
console.info(TAG, `aboutToAppear = ` + dailyPaperTopicPageId)
const currentTime = new Date().getTime()
...
...
@@ -110,6 +121,7 @@ export struct MorningEveningPaperComponent {
Logger.info(TAG, "pageInfoBean dateTime = " + dateTime)
Logger.info(TAG, "pageInfoBean subTitle = " + this.subTitle)
this.setComponentBgColor(pageInfoBean.backgroundImgUrl)
let compInfoBean = await MorningEveningViewModel.getMorningEveningCompInfo(pageInfoBean?.id, pageInfoBean?.groups[0]?.id, currentTime + "", pageInfoBean?.topicInfo?.topicId)
// this.compInfoBean = compInfoBean
...
...
@@ -130,6 +142,30 @@ export struct MorningEveningPaperComponent {
} catch (exception) {
}
}
async setComponentBgColor(imageUrl: string) {
// 图片转换为PixelMap对象
// const pixelMap: image.PixelMap = await image2PixelMap(item.icon);
const imageSource: image.ImageSource | undefined = await getPicture(imageUrl);
if (imageSource) {
this.pickColor(imageSource)
}
}
private async pickColor(imageSource: image.ImageSource | undefined) {
if (imageSource) {
const pixelMap: image.PixelMap = await imageNet2PixelMap(imageSource);
effectKit.createColorPicker(pixelMap, (err, colorPicker) => {
let color = colorPicker.getMainColorSync();
// 将取色器选取的color示例转换为十六进制颜色代码
this.mixedBgColor = "#" + color.alpha.toString(16) + color.red.toString(16) + color.green.toString(16) + color.blue.toString(16);
});
}
}
onPageHide() {
...
...
@@ -175,7 +211,9 @@ export struct MorningEveningPaperComponent {
.width('100%')
// .backgroundColor('#000080')
// .backgroundColor(Color.Black)
.backgroundColor(this.pageInfoBean?.backgroundColor ?? Color.Black)
// .backgroundColor(this.pageInfoBean?.backgroundColor ?? Color.Black)
.backgroundColor(this.mixedBgColor ?? Color.Black)
.padding({ top: this.topSafeHeight })
}
@Builder
...
...
sight_harmony/features/wdComponent/src/main/ets/utils/ColorUtils.ets
0 → 100644
View file @
fead880
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* RGB颜色类型
*/
export interface ColorRgb {
// 红色(Red)
red: number;
// 绿色(Green)
green: number;
// 蓝色(Blue)
blue: number;
// 透明度
alpha: number;
}
/**
* HSV颜色类型
*/
export interface ColorHsv {
// 色调
hue: number;
// 饱和度
saturation: number;
// 明度
value: number;
// 透明度
alpha: number;
}
// RGB颜色最大值
const MAX_RGB_VALUE: number = 255.0;
export class ColorUtils {
/**
* RGB格式转换为HSV格式,公式参考resources/base/media/rgb_to_hsv.png
* @param color RGB像素值
* @returns HSV像素值
*/
public static rgb2hsv(color: ColorRgb): ColorHsv {
// RGB颜色取值范围是0~255,需要转换为0~1的浮点数
const red: number = color.red / MAX_RGB_VALUE;
const green: number = color.green / MAX_RGB_VALUE;
const blue: number = color.blue / MAX_RGB_VALUE;
const max: number = Math.max(red, green, blue);
const min: number = Math.min(red, green, blue);
const delta: number = max - min;
// 色调
let hue: number = 0;
// 饱和度
let saturation: number = 0;
// 明度
let value: number = 0;
// 计算hue值
if (max === min) {
hue = 0;
} else if (Math.abs(max - red) < Number.EPSILON) {
hue = (green >= blue ? ((green - blue) / delta) * 60 : ((green - blue) / delta) * 60 + 360);
} else if (Math.abs(max - green) < Number.EPSILON) {
hue = (((blue - red) / delta) + 2) * 60;
} else if (Math.abs(max - blue) < Number.EPSILON) {
hue = (((red - green) / delta) + 4) * 60;
}
// 计算saturation值
saturation = (max === 0 ? 0 : delta / max);
// 计算value值
value = max;
return {
hue: hue,
saturation: saturation,
value: value,
alpha: color.alpha
}
}
/**
* HSV格式转换为RGB格式,公式可参考resources/base/media/hsv_to_rgb.png
* @param color HSV像素值
* @returns RGB像素值
*/
public static hsv2rgb(color: ColorHsv): ColorRgb {
// hue取值范围是0~360°,每60°为一个区间,每个区间中RGB计算方式不同
const h60: number = color.hue / 60;
// 向下取整
const h60f: number = Math.floor(h60);
const hi: number = h60f % 6;
const f: number = h60 - h60f;
// 计算不同hue区间里面的RGB值
const p: number = color.value * (1 - color.saturation);
const q: number = color.value * (1 - f * color.saturation);
const t: number = color.value * (1 - (1 - f) * color.saturation);
let red: number = 0.0;
let green: number = 0.0;
let blue: number = 0.0;
// 根据区间,计算RGB的值
if (hi === 0) {
red = color.value;
green = t;
blue = p;
} else if (hi === 1) {
red = q;
green = color.value;
blue = p;
} else if (hi === 2) {
red = p;
green = color.value;
blue = t;
} else if (hi === 3) {
red = p;
green = q;
blue = color.value;
} else if (hi === 4) {
red = t;
green = p;
blue = color.value;
} else if (hi === 5) {
red = color.value;
green = p;
blue = q;
}
// RGB取值范围是0~255,需要通过计算
return {
red: Math.floor(red * MAX_RGB_VALUE),
green: Math.floor(green * MAX_RGB_VALUE),
blue: Math.floor(blue * MAX_RGB_VALUE),
alpha: color.alpha
}
}
/**
* RGB颜色转为整数值
* @param color RGB像素值
* @returns 整数
*/
public static rgbToNumber(color: ColorRgb): number {
const tmpColor = ((color.alpha << 24) | (color.red << 16) | (color.green << 8) | color.blue)
return tmpColor;
}
/**
* 整数值转为RGB颜色值
* @param color 整数
* @returns RGB格式像素
*/
public static numberToRgb(color: number): ColorRgb {
return {
red: (color & 0xFF0000) >> 16,
green: (color & 0xFF00) >> 8,
blue: (color & 0xFF),
alpha: (color & 0xFF000000) >> 24
}
}
}
\ No newline at end of file
...
...
sight_harmony/features/wdComponent/src/main/ets/utils/ImageUtils.ets
0 → 100644
View file @
fead880
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { resourceManager } from '@kit.LocalizationKit';
import { image } from '@kit.ImageKit';
import { ColorHsv, ColorRgb, ColorUtils } from './ColorUtils';
import { http } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import ResponseCode from '@ohos.net.http';
const resourceMgs: resourceManager.ResourceManager = getContext(this).resourceManager;
const PIXEL_MAP_SIZE_WIDTH: number = 40;
const PIXEL_MAP_SIZE_HEIGHT: number = 40;
/**
* 图片转换为PixelMap对象
* @param icon 图片地址,模拟数据存放于rawfile文件夹中
* @returns 图片转换的PixelMap对象
*/
export async function image2PixelMap(icon: string): Promise<image.PixelMap> {
// 读取rawfile文件夹下的文件
const rawFileDescriptor: resourceManager.RawFileDescriptor = resourceMgs.getRawFdSync(icon);
const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
// 通过ImageSource对象创建PixelMap对象,使用BGRA_8888格式,由4个字节表示一个像素
const pixelMap: Promise<PixelMap> = imageSource.createPixelMap({
editable: false,
desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
// 高性能知识点:经测试,将图片的宽和高设置为40像素时,既可以保证提取颜色的准确性,也可以保证计算颜色的速度。
desiredSize: { width: PIXEL_MAP_SIZE_WIDTH, height: PIXEL_MAP_SIZE_HEIGHT }
})
return pixelMap;
}
/**
* 通过http的request方法从网络下载图片资源
*/
// export async function getPicture() {
// const url:string='https://rmrbcmsonline.peopleapp.com/upload/image/202312/rmrb_86691703594454.png'
// http.createHttp()
// .request(url,
// (error: BusinessError, data: http.HttpResponse) => {
// // if (error) {
// // // 下载失败时弹窗提示检查网络,不执行后续逻辑
// // promptAction.showToast({
// // message: $r('app.string.image_request_fail'),
// // duration: 2000
// // })
// // return;
// // }
// // transcodePixelMap(data);
// // 判断网络获取到的资源是否为ArrayBuffer类型
// // if (data.result instanceof ArrayBuffer) {
// // this.imageBuffer = data.result as ArrayBuffer;
// // }
// if (ResponseCode.ResponseCode.OK === data.responseCode) {
// const imageData: ArrayBuffer = data.result as ArrayBuffer;
// // 通过ArrayBuffer创建图片源实例。
// const imageSource: image.ImageSource = image.createImageSource(imageData);
// return imageSource
// // const options: image.InitializationOptions = {
// // 'alphaType': 0, // 透明度
// // 'editable': false, // 是否可编辑
// // 'pixelFormat': 3, // 像素格式
// // 'scaleMode': 1, // 缩略值
// // 'size': { height: 100, width: 100 }
// // }; // 创建图片大小
//
// // 通过属性创建PixelMap
// // imageSource.createPixelMap(options).then((pixelMap: PixelMap) => {
// // // this.image = pixelMap;
// // });
// // imageNet2PixelMap(imageSource)
// }
// }
// )
// }
// 假设http和image是之前正确导入或定义的模块
export async function getPicture(imageUrl: string): Promise<image.ImageSource | undefined> {
// const url: string = 'https://rmrbcmsonline.peopleapp.com/upload/image/202312/rmrb_86691703594454.png';
const url: string = 'https://rmrbcmsonline.peopleapp.com/upload/image/201912/rmrb_24141576767688.png?x-oss-process=image/resize,w_550/quality,q_90/format,jpg';
return new Promise((resolve, reject) => {
http.createHttp().request(url, (error: BusinessError, data: http.HttpResponse) => {
if (error) {
// 处理错误情况,比如网络错误
console.error("Download error", error);
// 你可以选择reject(error)或者根据需要调整
reject(error);
return;
}
if (ResponseCode.ResponseCode.OK === data.responseCode) {
const imageData: ArrayBuffer = data.result as ArrayBuffer;
// 通过ArrayBuffer创建图片源实例
const imageSource: image.ImageSource = image.createImageSource(imageData);
resolve(imageSource); // 成功时解析Promise
} else {
// 处理其他响应码
reject(new Error('Invalid response code'));
}
});
});
}
/**
* 使用createPixelMap将ArrayBuffer类型的图片装换为PixelMap类型
* @param data:网络获取到的资源
*/
function transcodePixelMap(data: http.HttpResponse) {
if (ResponseCode.ResponseCode.OK === data.responseCode) {
const imageData: ArrayBuffer = data.result as ArrayBuffer;
// 通过ArrayBuffer创建图片源实例。
const imageSource: image.ImageSource = image.createImageSource(imageData);
// const options: image.InitializationOptions = {
// 'alphaType': 0, // 透明度
// 'editable': false, // 是否可编辑
// 'pixelFormat': 3, // 像素格式
// 'scaleMode': 1, // 缩略值
// 'size': { height: 100, width: 100 }
// }; // 创建图片大小
// 通过属性创建PixelMap
// imageSource.createPixelMap(options).then((pixelMap: PixelMap) => {
// // this.image = pixelMap;
// });
// imageNet2PixelMap(imageSource)
}
}
/**
* 图片转换为PixelMap对象
* @param icon 图片地址,模拟数据存放于rawfile文件夹中
* @returns 图片转换的PixelMap对象
*/
export async function imageNet2PixelMap(imageSource: image.ImageSource): Promise<image.PixelMap> {
// 读取rawfile文件夹下的文件
// const rawFileDescriptor: resourceManager.RawFileDescriptor = resourceMgs.getRawFdSync(icon);
// const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
// 通过ImageSource对象创建PixelMap对象,使用BGRA_8888格式,由4个字节表示一个像素
const pixelMap: Promise<PixelMap> = imageSource.createPixelMap({
editable: false,
desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
// 高性能知识点:经测试,将图片的宽和高设置为40像素时,既可以保证提取颜色的准确性,也可以保证计算颜色的速度。
desiredSize: { width: PIXEL_MAP_SIZE_WIDTH, height: PIXEL_MAP_SIZE_HEIGHT }
})
return pixelMap;
}
/**
* 查找数组中出现次数最多的像素
* @param allPixels 像素数组
* @returns 出现次数最多的像素
*/
export function findMaxPixel(allPixels: number[]): number {
// 遍历数组,将像素放到一个Map中,key是像素值,value是该像素值出现的次数
const map: Map<number, number> = new Map();
allPixels.forEach((pixel: number) => {
if (map.has(pixel)) {
map.set(pixel, map.get(pixel)! + 1);
} else {
map.set(pixel, 1);
}
})
// 查找出现次数最多的像素
let maxPixel: number = 0;
let maxTimes: number = 0;
map.forEach((value: number, key: number) => {
if (value >= maxTimes) {
maxTimes = value;
maxPixel = key;
}
})
return maxPixel;
}
/**
* 修改HSV格式的颜色值,可根据业务需求或者UI设计自行修改S和V的值,此处只举例进行说明
* @param color HSV格式的颜色
*/
export function modifySVValue(color: ColorHsv): void {
if (color.hue > 0 && color.hue <= 60) {
color.saturation = 0.12;
color.value = 0.93;
} else if (color.hue > 60 && color.hue <= 190) {
color.saturation = 0.08;
color.value = 0.91;
} else if (color.hue > 190 && color.hue <= 270) {
color.saturation = 0.1;
color.value = 0.93;
} else {
color.saturation = 0.08;
color.value = 0.93;
}
}
/**
* 遍历所有像素,并放到一个数组中
* @param pixelMap 图片的PixelMap对象
* @returns 包含图片所有像素的数组
*/
export async function traverseAllPixel(pixelMap: image.PixelMap): Promise<number[]> {
// PixelMap对象使用BGRA_8888格式,由4个字节表示一个像素,并且宽和高都设置为40,所以此处ArrayBuffer的length就是40*40*4
const pixelArrayBuffer: ArrayBuffer = new ArrayBuffer(40 * 40 * 4);
// 将PixelMap对象读入ArrayBuffer中
await pixelMap.readPixelsToBuffer(pixelArrayBuffer);
const allPixels: number[] = [];
// ArrayBuffer是一个二进制的数组,无法直接读取内容,需要转换为整型数组才可以正常使用,此处使用Uint8Array(8 位无符号整型数组)
const unit8Pixels: Uint8Array = new Uint8Array(pixelArrayBuffer);
// 遍历unit8Pixels,取出每个像素的red、green、blue、alpha颜色值,放到一个ColorRgb对象中,然后将ColorRgb转换为数字,放入数组中
for (let i = 0; i < unit8Pixels.length; i += 4) {
// 如果是透明色,则不放入数组中,否则可能导致计算出来的出现次数最多的像素是透明色
if (unit8Pixels[i] === 0 && unit8Pixels[i+1] === 0 && unit8Pixels[i+2] === 0 && unit8Pixels[i+3] === 0) {
continue;
}
// BGRA_8888格式,B表示Blue,G表示Green,R表示Red,A表示透明度,此处根据顺序提取B、R、G、A
const rgb: ColorRgb = {
red: unit8Pixels[i+2],
green: unit8Pixels[i+1],
blue: unit8Pixels[i],
alpha: unit8Pixels[i+3]
}
// 高性能知识点:直接将ColorRgb放入数组或者Map中会影响计算出现次数最多的像素的速度,所以需要使用rgbToNumber方法转换为整数
allPixels.push(ColorUtils.rgbToNumber(rgb));
}
return allPixels;
}
\ No newline at end of file
...
...
Please
register
or
login
to post a comment