2025-12-30 19:09:47
signals 提供新的数据绑定和变更检测机制。从 angular 17 就开始引入了,
优点:
缺点:
基本的绑定,可读可写。
angular 17 之前:
@Component({
standalone: false,
selector: 'app-example',
template: `{{ text }}`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
public text = 'hi';
public tap() {
this.text = 'hello';
}
}
angular 17 之后:
@Component({
standalone: false,
selector: 'app-example',
template: `{{ text() }}`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
public readonly text = signal<string>('hi');
public tap() {
this.text.set('hello');
this.text.update(v => {
return 'hello';
});
}
}
set 或 update 更新值。update 更新某个属性,必须生成新的对象,才能有效检测
public readonly data = signal({
a: 1,
b: 2
});
// 错误示范
this.data.update(v => {
v.a = 3
return v;
});
// 正确使用 this.data.update(v => { v.a = 3; return {...v}; // 或者 return {...v, a: 3}; });
3. 当值时数组时,使用 `update` 新增或删除某一项
```ts
public readonly items = signal<number[]>([]);
// 错误示范
this.items.update(v => {
v.push(1);
v.pop();
return v;
});
// 正确使用
this.items.update(v => {
v.push(1);
return [...v];
// 或者
return [...v, 1];
});
input 相当于 @Input(), 但是可读不可写,增加了自定义转换
output 相当于 @Output(),
model 相对于 @Input() + @Output(),可读可写
angular 17 之前:
@Component({
standalone: false,
selector: 'app-example',
template: `{{ text }}`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
@Input() public text = 'hi';
@Output() public textChange = new EventEmitter();
}
angular 17 之后:
@Component({
standalone: false,
selector: 'app-example',
template: `{{ text() }}`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
public readonly text = input('hi', {tranform: parseInt});
public readonly textChange = output();
public readonly text = model('');
}
@Component({
standalone: false,
selector: 'app-example',
template: `<input type="number" [field]="form.a">`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
public readonly form = form(sinal({
a: 1
}), schemaPath => {
required(schemaPath.a);
})
}
使用 [field] 精选表单绑定,不能自定义 name 属性,
name 属性自动生成 类似于 [项目名].form1.a,如果介意 name,则推荐使用 原本 FormBuilder 方式select 的值为 string,input type=checkbox 为 boolean, type=number 为 number, 其他则必须为 string
检测 值的变化,替代 ngOnChanges
@Component({
standalone: false,
selector: 'app-example',
template: `{{ text() }}`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
public readonly text = model('');
constructor() {
let previousText = '';
effect(() => {
this.text();
// 当 text 发生变化时,TODO
previousText = this.text(); // 使用此方法实现值的前后变化检测
});
}
}
关联变化,提供值变化前后对比
angular 17 之前:
@Component({
standalone: false,
selector: 'app-example',
template: `{{ text }} {{ twoText }}`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
public text = 'hi';
public get twoText() {
return this.text + ', two';
}
public tap() {
this.text = 'hello';
}
}
angular 17 之后:
@Component({
standalone: false,
selector: 'app-example',
template: `{{ text() }} {{ twoText() }}`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
public readonly text = signal<string>('hi');
public readonly twoText = computed(() => {
return this.text() + ', two';
});
public tap() {
this.text.set('hello');
this.text.update(v => {
return 'hello';
});
}
}
@HostBinding 无法与 signal 等使用
@HostBinding('class.open') // 无效
public readonly toggle = model(false);
可以使用 effect 同步或
@Component({
standalone: false,
selector: 'app-example',
template: ``,
styleUrls: ['./example.component.scss'],
host: {
'[class.open]': 'toggle()'
}
})
export class ExampleComponent {
public readonly toggle = model(false);
}
2025-09-12 00:02:25
| 数据类型 | 子节数 | 说明 |
|---|---|---|
| byte | 1 | 占 8 个二进制字符,特殊知识:以 0x80 为分界 |
| sbyte | 1 | 占 8 个二进制字符 |
| short | 2 | |
| ushort | 2 | |
| int | 4 | |
| uint | 4 | |
| float | 4 | |
| long | 8 | |
| ulong | 8 | |
| double | 8 | |
| char | 1 、 2 | 在 c 等低级语言中占 1 个子节,在 c# 等高级语言中占 2 子节 |
| string | 无固定长度 | 一种是定义长度,再取长度的字节数转,另一种是以 0x0 为结束符 |
| leb128 | 无固定长度 | 每次读一个子节, 子节值大于等于 0x80 表示有需要读下一个子节 |
主要为大端小端,主要是指有多个固定字节数的数据类型的字节写入顺序不同, byte sbyte 只有一个字节,没有大小端之分;string char leb128 都不固定长度,依赖前一个字节指示是否有下一个字节,所以无法分大小端。
大端(BigEndian, 简写BE),高位写在前面,反转过来写的意思,例如 (ushort)1 转成字节数组是 [0x1, 0x0] 写入流时是先写 0x0 在写入 0x1
小端(LittleEndian, 简写LE),低位写在前面,顺着字节数组写的意思,例如 (ushort)1 转成字节数组是 [0x1, 0x0] 写入流时是先写 0x1 在写入 0x0
一般来说:网络字节流的是大端,计算机文件流是小端。
Unicode 固定文件头 0xFF 0xFE,大端则是 0xFE 0xFF
UTF8 分有头和无头,有固定头 0xEF 0xBB 0xBF
无头编码,主要根据 char(两个字节) 存储方式判断,一般来说 0x0-0x7F 是指占用一个字节,0x80-0xFF 根据定义可以指示多个字节
UTF8
| 字节数 | 字节分布 |
|---|---|
| 1 | 0x00-0x7F |
| 2 | 0xC2-0xDF 0x80-0xBF |
| 3 | 0xE0 0xA0-0xBF 0x80-0xBF |
| 3 | 0xE1-0xEC 0x80-0xBF 0x80-0xBF |
| 3 | 0xED 0x80-0x9F 0x80-0xBF |
| 3 | 0xEE-0xEF 0x80-0xBF 0x80-0xBF |
| 4 | 0xF0 0x90-0xBF 0x80-0xBF 0x80-0xBF |
| 4 | 0xF1-0xF3 0x80-0xBF 0x80-0xBF 0x80-0xBF |
| 4 | 0xF4 0x80-0x8F 0x80-0xBF 0x80-0xBF |
一般是固定的头,固定几个字节指示
| 算法类型 | 代表算法 | 适用场景 |
|---|---|---|
| 字典编码 | LZ77, LZ78, LZW | 重复数据压缩(文本) |
| 熵编码 | Huffman, 算术编码 | 通过加入长度来表示重复字节或字节数组 |
图片无损压缩: 对单个像素点(R|G|B|A)进行处理,例如不透明图片就可以只保存(R|G|B)三个颜色值,更进一步有单个色值的,例如只保存(R)通道
一般只针对特殊领域,例如图像
图象的有损压缩:分成一个个(width*height)小的区域进行近似关联处理,常见的是分成 (4*4) 的小区域,每个区域处理成 8 个字节
加密算法
常见的加密算法:
这些复杂的加密算法只适用于文件少且重要的,速度较慢、内存占用较高
一般对速度和性能有要求的就简单的使用xor(异或处理)
0x0, 不是的话就肯定在前面标记了长度,byte 再考虑 leb128 再 uint int long
leb128, 那就可以肯定 其他长度都用的 leb128, 甚至 uint ulong 等值也转成了 leb128 0x0, 0x0 多用于补位,例如数据类型固定占用多个子节,不足就是 0x0,还有就是补数据内容的长度2025-03-06 22:37:16
每条记录可以看作一个数据块
struct Chunk {
int id;
long offset;
long length;
}
顺序读取每一块并记录每一块的起始位置和数据长度。
但是更新写入就有点复杂了。
先读取数据块,并标记数据块是原始文件。
struct Chunk {
int id;
bool isTemporarySource;
long offset;
long length;
}
所有未保存的删除写入更新,都是发生在临时文件中。
2024-11-22 18:33:46
本文基于透明图片的透明度获取轮廓;如不透明图片获取轮廓需要先把图片转成灰度图片,根据灰度值获取轮廓。
o c o o
o o b o
o a o o
o o o
// 从 a 找到 b,方向为 1 (0 是正上方)
// 那个 b 就在 a 的 1 方向,a 就在 b 的 5方向
// 但 b 的 6 方向已经被 a 找过了, 所以 b 的起始方向就是 7
// 总结 b 在 a 的 n 方向,则 b 的起始方向为 n + 6
/// <summary>
/// 边界算法
/// </summary>
/// <returns></returns>
private static SKPath? TraceContour(SKPixmap pixMap, int beginX, int beginY)
{
var path = new SKPath();
path.MoveTo(beginX, beginY);
var directItems = new int[][] {
[0, -1], [1, -1],
[1, 0],
[1, 1], [0, 1], [-1, 1],
[-1, 0], [-1, -1]
};
var beginDirect = 0;
var isBegin = false;
var curX = beginX;
var curY = beginY;
while (!isBegin)
{
var i = 0;
var direct = beginDirect;
var hasPoint = false;
while (i ++ <= directItems.Length)
{
var x = curX + directItems[direct][0];
var y = curY + directItems[direct][1];
// 判断点是否是透明像素点
if (IsTransparent(pixMap, x, y))
{
direct = (direct + 1) % directItems.Length;
continue;
}
hasPoint = true;
curX = x;
curY = y;
if (curX == beginX && curY == beginY)
{
isBegin = true;
path.Close();
}
else
{
path.LineTo(curX, curY);
}
beginDirect = (direct + 6) % directItems.Length;
break;
}
if (!hasPoint)
{
// 所有方向都没有找到下一个不透明点,表明这就是一个孤点
return null;
}
}
return path;
}
/// <summary>
/// 物体轮廓获取
/// </summary>
public class ImageContourTrace
{
public ImageContourTrace()
{
}
public ImageContourTrace(bool isOutline)
{
IsOutline = isOutline;
}
/// <summary>
/// 外边框,即靠近物体的透明区域
/// </summary>
public bool IsOutline { get; set; }
/// <summary>
/// 是否需要获取一个点
/// </summary>
public bool IsAllowDot { get; set; }
/// <summary>
/// 获取图片上所有物体轮廓
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public async Task<SKPath[]> GetContourAsync(SKBitmap image, CancellationToken token = default)
{
using var imagePixMap = image.PeekPixels();
return await GetContourAsync(imagePixMap, token);
}
/// <summary>
/// 获取图片上所有物体轮廓
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public async Task<SKPath[]> GetContourAsync(SKImage image, CancellationToken token = default)
{
using var imagePixMap = image.PeekPixels();
return await GetContourAsync(imagePixMap, token);
}
/// <summary>
/// 获取图片上所有物体轮廓
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public Task<SKPath[]> GetContourAsync(SKPixmap pixMap, CancellationToken token = default)
{
return Task.Factory.StartNew(() => {
return GetContour(pixMap, token);
}, token);
}
/// <summary>
/// 获取所有物体的轮廓
/// </summary>
/// <param name="pixMap"></param>
/// <returns></returns>
public SKPath[] GetContour(SKPixmap pixMap, CancellationToken token = default)
{
var items = new List<SKPath>();
for (var i = 0; i < pixMap.Height; i++)
{
for (var j = 0; j < pixMap.Width; j++)
{
if (token.IsCancellationRequested)
{
return [..items];
}
if (IsTransparent(pixMap, j, i) || Contains(items, j, i))
{
continue;
}
var path = GetContour(pixMap, j, i);
if (path is null)
{
continue;
}
items.Add(path);
}
}
return [.. items];
}
/// <summary>
/// 根据坐标获取轮廓边界算法
/// </summary>
/// <param name="pixMap"></param>
/// <param name="beginX"></param>
/// <param name="beginY"></param>
/// <returns></returns>
public SKPath? GetContour(SKPixmap pixMap, int beginX, int beginY)
{
var path = new SKPath();
path.MoveTo(beginX, beginY - (IsOutline ? 1 : 0));
var directItems = new int[][] {
[0, -1], [1, -1],
[1, 0],
[1, 1], [0, 1], [-1, 1],
[-1, 0], [-1, -1]
};
var beginDirect = 0;
var isBegin = false;
var curX = beginX;
var curY = beginY;
while (!isBegin)
{
var i = 0;
var direct = beginDirect;
var hasPoint = false;
while (i++ <= directItems.Length)
{
var x = curX + directItems[direct][0];
var y = curY + directItems[direct][1];
if (IsTransparent(pixMap, x, y))
{
direct = (direct + 1) % directItems.Length;
if (IsOutline)
{
path.LineTo(x, y);
}
continue;
}
hasPoint = true;
curX = x;
curY = y;
if (curX == beginX && curY == beginY)
{
isBegin = true;
path.Close();
}
else if (!IsOutline)
{
path.LineTo(curX, curY);
}
beginDirect = (direct + 6) % directItems.Length;
break;
}
if (!hasPoint)
{
if (IsOutline)
{
path.Close();
return path;
}
// 所有方向都没有不透明点,就是一个孤点
return IsAllowDot ? path : null;
}
}
return path;
}
private static bool Contains(IEnumerable<SKPath> items, int x, int y)
{
foreach (var item in items)
{
if (item.Contains(x, y))
{
return true;
}
}
return false;
}
private static bool IsTransparent(SKPixmap pixMap, int x, int y)
{
if (x < 0 || y < 0 || x >= pixMap.Width || y >= pixMap.Height)
{
return true;
}
return pixMap.GetPixelColor(x, y).Alpha == 0;
}
}
2024-08-19 18:54:04
/// <summary>
/// 把像素字节数组转图片
/// </summary>
/// <param name="buffer">像素数组,例如: [r, g, b, a, r, g, b, a ...]</param>
/// <param name="width">图片的宽度,例如: 512</param>
/// <param name="height">图片的高度,例如: 1024</param>
/// <param name="format">指定像素数组的组成方式,例如:SKColorType.Rgba8888</param>
/// <returns></returns>
public static SKBitmap Decode(byte[] buffer, int width, int height, SKColorType format)
{
var data = SKData.CreateCopy(buffer);
var newInfo = new SKImageInfo(width, height, format);
var bitmap = new SKBitmap();
bitmap.InstallPixels(newInfo, data.Data);
return bitmap;
}
public static SKImage Decode(byte[] buffer, int width, int height, SKColorType format)
{
var newInfo = new SKImageInfo(width, height, format);
var data = SKData.CreateCopy(buffer);
return SKImage.FromPixels(newInfo, data);
}
SkiaSharp 默认支持 png jpg 等文件格式,但是一些不支持的文件格式怎么显示呢?
例如:pvr 格式就不能直接解码。
SkiaSharp 支持,所有支持的颜色编码方式在 SKColorType 中SKColorType.Rgba8888, 即:每个像素占四个字节,分别为 [Red,Green,Blue,Alpha],总字节为 宽*高*4, 以逐行横行存储, (行*宽+列)*4 获取像素点的位置Decode(转换后的数据, 宽, 高, SKColorType.Rgba8888) 即可A: SkiaSharp 是线程安全的,两个线程不应访问同一个对象。SKBitmap 只能在UI主线程中操作,SKImage 则可以在所有子线程中操作
2024-08-04 18:43:01
NAS设备:绿联DXP4800 Plus
NAS系统:UGOS PRO

无法直接 DockerHub 可以使用 阿里云的镜像加速


搜索 gogs 下载

配置自动重启

配置文件保存位置

配置端口映射

访问网页进行GOGS配置

图片来源:https://www.cnblogs.com/yuexiaoyun/articles/11946103.html
图片来源:https://www.cnblogs.com/yuexiaoyun/articles/11946103.html
Q: nas重启后网址无法打开
A:可能是gogs安装时,端口配置错误,可以到gogs的文件保存位置下找到 gogs/conf/app.ini 文件修改 [server]HTTP_PORT为 3000 即可

