WPF 录屏软件研发心得及思路分享(已结束开发)

时间:2022-09-25 23:03:57

最近由于工程需要开始研发基于Windows的自动录屏软件,很多细节很多功能需要处理,毕竟一个完美的录屏软件不是你随随便便就可以写出来的。首先参考了大部分的录屏软件,在研发的过程中遇到了很多的问题;比如-视频加载、麦克风加载、麦克风音量调节、视频播放进度控、视频音量控制、等等很多细节部分都需要好好规划才能开始做。录屏采用的是视频帧的思维逻辑进行编写的。

目前已经基本上成型,基于WPF采用了Model - View框架进行动态加载,每个线程与线程之间采用Async异步执行,并使用线程等待;录屏基本功能包含了(展示历史录屏记录、删除、录屏、视频编码、视频播放及删除、麦克风调用(音量调节-跟随系统)、加载视频(拖拉-旋转)、系统遮罩 等);编码的核心是采用FFMPEG(这个工具真的非常强大);

这边提供几个核心代码仅供参考:

1-难点:系统遮罩核心方法(使用Windows API):

         /// <summary>
/// 视图模型属性改变
/// </summary>
/// <param name="sender">
/// The sender.
/// </param>
/// <param name="propertyChangedEventArgs">
/// 属性改变事件参数
/// </param>
private void ViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
if (propertyChangedEventArgs.PropertyName == "IsRecording")
{
this.Locked = this.ViewModel.IsRecording;
if (this.ViewModel.IsRecording)
{
var hwnd = new WindowInteropHelper(this).Handle;
NativeWindowHelper.SetWindowExTransparent(hwnd);
}
} if (propertyChangedEventArgs.PropertyName == "IsFullScreen")
{
this.IsFullScreen = this.ViewModel.IsFullScreen;
}
}

改变属性的时候触发

         #region Constants

         /// <summary>
/// The gw l_ exstyle.
/// </summary>
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore",
Justification = "Reviewed. Suppression is OK here.")]
private const int GWL_EXSTYLE = -; /// <summary>
/// The w s_ e x_ transparent.
/// </summary>
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore",
Justification = "Reviewed. Suppression is OK here.")]
private const int WS_EX_TRANSPARENT = 0x00000020; #endregion #region Public Methods and Operators /// <summary>
/// 窗口前置透明设置命令
/// </summary>
/// <param name="hwnd">
/// The hwnd.
/// </param>
public static void SetWindowExTransparent(IntPtr hwnd)
{
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
} #endregion #region Methods /// <summary>
/// The get window long.
/// </summary>
/// <param name="hwnd">
/// The hwnd.
/// </param>
/// <param name="index">
/// The index.
/// </param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hwnd, int index); /// <summary>
/// The set window long.
/// </summary>
/// <param name="hwnd">
/// The hwnd.
/// </param>
/// <param name="index">
/// The index.
/// </param>
/// <param name="newStyle">
/// The new style.
/// </param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle); #endregion

API方法

2-难点:麦克风获取及控制

<Slider x:Name="volumeSlider" Grid.Column="" Grid.ColumnSpan="" Grid.Row="" Width="" Height="" Minimum="" Maximum="" Value="" VerticalAlignment="Center" />
  //定义一个获取之前拉动时候的value值,然后跟当前的value对比,选择触发
private bool isUserChangeVolume = true;
private VolumeControl volumeControl;
//private DispatcherTimer volumeControlTimer; /// <summary>
/// 加载拖动条的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void volumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (isUserChangeVolume)
{
volumeControl.MasterVolume = volumeSlider.Value;
}
} private void InitializeAudioControl()
{
volumeControl = VolumeControl.Instance;
volumeControl.OnAudioNotification += volumeControl_OnAudioNotification;
volumeControl_OnAudioNotification(null, new AudioNotificationEventArgs() { MasterVolume = volumeControl.MasterVolume }); //volumeControlTimer = new DispatcherTimer();
//volumeControlTimer.Interval = TimeSpan.FromTicks(150);
//volumeControlTimer.Tick += volumeControlTimer_Tick;
} void volumeControl_OnAudioNotification(object sender, AudioNotificationEventArgs e)
{
this.isUserChangeVolume = false;
try
{
this.Dispatcher.Invoke(new Action(() => { volumeSlider.Value = e.MasterVolume; }));
}
catch { }
this.isUserChangeVolume = true;
} void volumeControlTimer_Tick(object sender, EventArgs e)
{
//获取系统主声道、左声道、右声道音量值
//double[] information = volumeControl.AudioMeterInformation;
//mMasterPBar.Value = information[0];
//mLeftPBar.Value = information[1];
//mRightPBar.Value = information[2];
}

3-难点:系统遮罩(其实也不能算难点,这个是API调用的时候颜色控制);

4-难点:视频旋转核心代码(已更新为方法8)

  /// <summary>
/// 旋转视频
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RotateCamera_bt(object sender, RoutedEventArgs e)
{
if (AnAngle > || AnAngle == )
{
AnAngle = ;
}
TransformGroup transformGroup = new TransformGroup(); ScaleTransform scaleTransform = new ScaleTransform();
scaleTransform.ScaleX = -;
transformGroup.Children.Add(scaleTransform); RotateTransform rotateTransform = new RotateTransform(AnAngle);
transformGroup.Children.Add(rotateTransform);
videoPlayer.RenderTransform = transformGroup;
AnAngle += ;
}

旋转视频代码

5-难点:录屏核心代码(这部分代码视频格式可以自行调整,颜色代码原理已经理解。)--已更新为方法10

  /// <summary>
/// Starts the recording.
/// </summary>
public void StartRecording()
{
this.notifyIcon.HideBalloonTip();
this.IsRecording = true; var fileName = string.Format("Recording {0}.mp4", DateTime.Now.ToString("yy-MM-dd HH-mm-ss"));
var outputFilePath = Path.Combine(this.settings.StoragePath, fileName);
this.fileViewModel = new ScreenGunFileViewModel(outputFilePath, RecordingStage.DoingNothing); var opts = new ScreenRecorderOptions(this.RecordingRegion)
{
DeleteMaterialWhenDone = true,
OutputFilePath = outputFilePath,
RecordMicrophone = this.UseMicrophone,
AudioRecordingDeviceNumber = this.settings.RecordingDeviceNumber
}; var progress = new Progress<RecorderState>(state => this.fileViewModel.RecordingStage = state.Stage);
this.recorder.Start(opts, progress);
}

录屏代码

6-难点:屏幕画框代码(采集X,Y坐标及遮幕的宽,高)

  /// <summary>
/// 设置初始区域
/// </summary>
private void SetupInitialRegion()
{
var cursorPos = System.Windows.Forms.Cursor.Position;
foreach (var screen in Screen.AllScreens)
{
if (screen.Bounds.Contains(cursorPos) == false)
{
continue;
} var regionWidth = (double)screen.Bounds.Width / ;
var regionHeight = (double)screen.Bounds.Height / ;
double x = ((double)screen.Bounds.Width / ) - (regionWidth / );
double y = ((double)screen.Bounds.Height / ) - (regionHeight / );
x -= this.virtualScreen.X - screen.Bounds.X;
y -= this.virtualScreen.Y - screen.Bounds.Y; this.startPosition = new Point(x, y);
this.endPosition = new Point(x + regionWidth, y + regionHeight);
this.UpdatePosition();
break;
}
}

7-放大缩小(根据屏幕大小范围随意拉伸缩小)  核心代码如下:

当你有摄像头长跟宽不一样的时候,旋转-缩小-放大然后根据给定的边缘坐标是一个非常头疼的事情,单单这个问题就使我加班到凌晨4点了,不过最终还是解决了;

   void resizer_Resize(object sender, ControlResizeEventArgs e)
{
if (!this.RectangleU.IsMouseCaptured) return;
if (AnAngle == || AnAngle == )
{
#region --竖直拉伸--
double Image_xx = ;
double Image_yy = ;
double point_xx = ;
double point_yy = ;
double point_center = Math.Abs(this.MainGrid.Width / - this.MainGrid.Height / );//当前中心点值
double actual_center = Math.Abs(this.videoPlayer.MinWidth / - this.videoPlayer.MinHeight / );//实际中心点,用于比较初始值 if (Math.Abs(Image_PointX) == )
{
point_xx = -;//初始化原点未动
}
else
{
//拖动到其他位置时偏移量(必须是固定值)
if (Image_PointX < -)
{
point_xx = -;
}
else
{
point_xx = Image_PointX;
}
} if (Math.Abs(Image_PointY) == )
{
point_yy = -;//初始化原点未动
}
else
{
//拖动到其他位置时偏移量(必须是固定值)
if (Image_PointY < -)
{
point_yy = -;
}
else
{
point_yy = Image_PointY;
} }
if (Math.Abs(point_xx) == )
{
Image_xx = videoPlayer.ActualHeight;
}
else
{
Image_xx = videoPlayer.ActualHeight + Math.Abs(Image_PointX) - ;
}
if (Math.Abs(point_yy) == )
{
Image_yy = videoPlayer.ActualWidth;
}
else
{
Image_yy = Math.Abs(Image_PointY) + videoPlayer.ActualWidth - ;
} //左右拉伸(只能往右拉伸)
if (e.LeftDirection.HasValue)
{
var value = videoPlayer.Height + e.HorizontalChange;
if (value > videoPlayer.MinHeight)
{
videoPlayer.Height = value;
MainGrid.Height = value;
if (videoPlayer.ActualHeight < value)
{
MainGrid.Height = videoPlayer.ActualHeight;
}
if (Image_xx >= RecordingArea.Width)
{
MainGrid.Height = videoPlayer.ActualHeight;
videoPlayer.Height = videoPlayer.ActualHeight;
}
}
}
//上下拉伸(只能往上拉伸)
if (e.TopDirection.HasValue)
{
var value = videoPlayer.Width + e.VerticalChange;
if (value > videoPlayer.MinWidth)
{
videoPlayer.Width = value;
MainGrid.Width = value;
if (videoPlayer.ActualWidth < value)
{
MainGrid.Width = videoPlayer.ActualWidth;
} if (Image_yy >= RecordingArea.Height)
{
MainGrid.Width = videoPlayer.ActualWidth;
videoPlayer.Width = videoPlayer.ActualWidth;
} }
} #region 调整位置 Matrix m = MainGrid.RenderTransform.Value; //初始值(-25,-25)-->(x,y)
if ((Image_xx >= RecordingArea.Width) || Image_yy >= RecordingArea.Height)
{ }
else
{
if (point_center >= actual_center)
{
// (point_center - actual_center)为x-y轴偏移量
//point_xx--point_yy为当前x,y轴坐标
m.OffsetX = point_xx - (point_center - actual_center);
m.OffsetY = point_yy - (point_center - actual_center); }
}
MainGrid.RenderTransform = new MatrixTransform(m);//重新定位 #endregion #endregion
}
else
{
#region --正常拉伸--
//左右拉伸(只能往右拉伸)
if (e.LeftDirection.HasValue)
{
var value = videoPlayer.Width + e.HorizontalChange;
if (value > videoPlayer.MinWidth)
{
videoPlayer.Width = value;
MainGrid.Width = value;
if (videoPlayer.ActualWidth < value)
{
MainGrid.Width = videoPlayer.ActualWidth;
}
if (Image_PointX + videoPlayer.ActualWidth >= RecordingArea.Width)
{
MainGrid.Width = videoPlayer.ActualWidth;
videoPlayer.Width = videoPlayer.ActualWidth;
}
}
}
//上下拉伸(只能往上拉伸)
if (e.TopDirection.HasValue)
{
var value = videoPlayer.Height + e.VerticalChange;
if (value > videoPlayer.MinHeight)
{
videoPlayer.Height = value;
MainGrid.Height = value;
if (videoPlayer.ActualHeight < value)
{
MainGrid.Height = videoPlayer.ActualHeight;
} if (Math.Abs(Image_PointY) + videoPlayer.ActualHeight >= RecordingArea.Height)
{
MainGrid.Height = videoPlayer.ActualHeight;
videoPlayer.Height = videoPlayer.ActualHeight;
} }
}
#endregion
}
}

放大缩小-分长宽不一致情况

8-旋转,核心代码如下:

        private void RotateCamera_bt(object sender, RoutedEventArgs e)
{
if (MainGrid.ActualWidth > SystemParameters.PrimaryScreenHeight)
{
return;
}
if (AnAngle > || AnAngle == )
{
AnAngle = ;
} TransformGroup transformGroup = new TransformGroup();
RotateTransform rotateTransform = new RotateTransform(AnAngle);
transformGroup.Children.Add(rotateTransform);
MainGrid.RenderTransform = transformGroup;
#region 特殊四个角反转需要变换长跟宽
//重新调整坐标坐标位置
Matrix m = MainGrid.RenderTransform.Value;
//求出中心点坐标
double point_xx = (this.MainGrid.ActualWidth) / - (this.MainGrid.ActualHeight) / ;
// Image_PointX,Image_Point为当前坐标
if (AnAngle == || AnAngle == )
{
if (AnAngle == )
{
RectangleU.VerticalAlignment = VerticalAlignment.Bottom;
RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
RectangleU.BorderThickness = new Thickness(, , , );
RectangleU.CornerRadius = new CornerRadius(, , , ); }
else
{
RectangleU.VerticalAlignment = VerticalAlignment.Top;
RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
RectangleU.BorderThickness = new Thickness(, , , );
RectangleU.CornerRadius = new CornerRadius(, , , ); }
if (!IschangeAngle)
{
if (Image_PointX <= -point_xx)
{
m.OffsetX = -point_xx;
}
else
{
m.OffsetX = Image_PointX - point_xx;
}
if (Image_PointY >= -point_xx)
{
m.OffsetY = -point_xx;
}
else
{
m.OffsetY = Image_PointY + point_xx;
} if (m.OffsetX >= this.RecordingArea.Width - this.videoPlayer.Width - point_xx)
{
m.OffsetX = m.OffsetX + point_xx * ; }
if (m.OffsetY >= -point_xx)
{
m.OffsetY = -point_xx;
}
}
else
{
//旋转为竖直拉到某个坐标时触发
if (Image_PointX <= -point_xx)
{
m.OffsetX = -point_xx;
}
else
{
m.OffsetX = Image_PointX;
}
if (Image_PointY >= point_xx)
{
m.OffsetY = -point_xx;
}
else
{
m.OffsetY = Image_PointY;
}
} if (this.MainGrid.Width >= this.RecordingArea.Height)
{
//触发
//相对于屏幕的x,y轴不变
m.OffsetX = -point_xx;
m.OffsetY = -point_xx; } }
else
{
if (AnAngle == )
{
RectangleU.VerticalAlignment = VerticalAlignment.Bottom;
RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
RectangleU.BorderThickness = new Thickness(, , , );
RectangleU.CornerRadius = new CornerRadius(, , , ); }
else
{
RectangleU.VerticalAlignment = VerticalAlignment.Top;
RectangleU.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
RectangleU.BorderThickness = new Thickness(, , , );
RectangleU.CornerRadius = new CornerRadius(, , , ); }
if (IschangeAngle)
{
if (this.MainGrid.Width >= this.RecordingArea.Height)
{
Image_PointX = ;
}
else
{
if (Image_PointX + this.videoPlayer.Width > this.RecordingArea.Width)
{
m.OffsetX = Image_PointX - point_xx;
}
else
{
m.OffsetX = Image_PointX + point_xx;
}
}
//旋转为竖直拉到某个坐标时触发
if (Image_PointY >= -point_xx)
{
m.OffsetY = ;
}
else
{
m.OffsetY = Image_PointY - point_xx;
} }
else
{ if (this.MainGrid.Width >= this.RecordingArea.Width)
{
m.OffsetX = ;
m.OffsetY = ;
}
else
{
//正常情况
if (Image_PointX <= )
{
m.OffsetX = ;
}
else
{
m.OffsetX = Image_PointX;
}
if (Image_PointY >= )
{
m.OffsetY = ;
}
else
{
m.OffsetY = Image_PointY;
}
}
}
}
//IschangeAngle = false;
//更换坐标位置
MainGrid.RenderTransform = new MatrixTransform(m); var x = Math.Min(this.startPosition.X, this.endPosition.X);
var y = Math.Min(this.startPosition.Y, this.endPosition.Y);
if (AnAngle == || AnAngle == )
{
if (this.MainGrid.Width >= this.RecordingArea.Height)
{
this.relativeRecordingArea = new Rect(x, y, this.MainGrid.Height, this.MainGrid.Width);
this.UpdateUI();
}
}
else
{
if (this.MainGrid.Width >= this.RecordingArea.Width)
{
this.relativeRecordingArea = new Rect(x, y, this.MainGrid.Width, this.MainGrid.Height);
this.UpdateUI();
}
}
//UpdatePosition(); AnAngle += ;
#endregion
}

旋转代码-分长宽不一致情况

9-不同屏幕百分比自适应边框-采用DPIX

这个稍微简单点:只要获取出每个屏幕差值即可。

dpiX = graphics.DpiX / 96;//当前屏幕的DPI然后除以正常值96得出的值即为扩展百分比

10-录屏核心代码:(不采用之前的位图编译,直接通过引用第三方插件)

WPF 录屏软件研发心得及思路分享(已结束开发)

通过AForge对FFMPEG进行录屏封装,我们可以轻松的录制想要录制的内容,关于录屏时间上则采用的是异步执行Timer。

  private void video_NewFrame(object sender, NewFrameEventArgs e)
{
//if (this.IScreenRecording)
//{
this.videoWriter.WriteVideoFrame(e.Frame);
//异步执行时间
this.stopWatchLabel.Dispatcher.Invoke(new Action
(() => this.stopWatchLabel.Text = string.Format
(@"{0}", this.stopWatch.Elapsed.ToString("hh\\:mm\\:ss"))));
//}
//else
//{
// stopWatch.Reset();
// videoStreamer.Stop();
// videoWriter.Close();
//}
}

11-比较重要的一步:任何商用的录屏软件都需要实现播放、暂停、继续功能,这款软件也不例外:

   /// <summary>
/// 点击之后更换图标并判断是否需要停止or启用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PauseOrRecording_Checked(object sender, RoutedEventArgs e)
{ //暂停计时
//暂停保存图片
//暂停保存麦克风
if (IScreenRecording)
{
this.stopWatch.Stop();//时间表
//this.videoStreamer.Stop();
this.videoStreamer.SignalToStop(); if (PassMediaMessage.IsrecordingSound)
{
//暂停 PassMediaMessage.IsrecordingSound = false;
PassMediaMessage.Is_Recording = false;
}
IScreenRecording = false;
}
else
{
if (this.IsMicrophone.IsChecked == true)
{
PassMediaMessage.IsrecordingSound = true;
PassMediaMessage.Is_Recording = true;
}
//启用(只是暂停并没有真正的释放)
this.stopWatch.Start();//时间表
this.videoStreamer.Start();
IScreenRecording = true;
}
}

12-由于我们软件是面向世界的,所以必须有增加世界12国语言支持,这边就不再详细贴出代码了。

13-整体效果展示:

WPF 录屏软件研发心得及思路分享(已结束开发)WPF 录屏软件研发心得及思路分享(已结束开发)WPF 录屏软件研发心得及思路分享(已结束开发)

WPF 录屏软件研发心得及思路分享(已结束开发) WPF 录屏软件研发心得及思路分享(已结束开发)WPF 录屏软件研发心得及思路分享(已结束开发)

WPF 录屏软件研发心得及思路分享(已结束开发)WPF 录屏软件研发心得及思路分享(已结束开发)

WPF 录屏软件研发心得及思路分享(已结束开发)的更多相关文章

  1. 用js实现web端录屏

    用js实现web端录屏 原创2021-11-14 09:30·无意义的路过 随着互联网技术飞速发展,网页录屏技术已趋于成熟.例如可将录屏技术运用到在线考试中,实现远程监考.屏幕共享以及录屏等:而在我们 ...

  2. 手游录屏直播技术详解 &vert; 直播 SDK 性能优化实践

    在上期<直播推流端弱网优化策略 >中,我们介绍了直播推流端是如何优化的.本期,将介绍手游直播中录屏的实现方式. 直播经过一年左右的快速发展,衍生出越来越丰富的业务形式,也覆盖越来越广的应用 ...

  3. Android实现录屏直播(三)MediaProjection &plus; VirtualDisplay &plus; librtmp &plus; MediaCodec实现视频编码并推流到rtmp服务器

    请尊重分享成果,转载请注明出处,本文来自Coder包子哥,原文链接:http://blog.csdn.net/zxccxzzxz/article/details/55230272 Android实现录 ...

  4. C&num;实现录音录像录屏源码

    以前写过两篇录音和录像的文章(实现语音视频录制.在服务器端录制语音视频),最近有朋友问,如果要实现屏幕录制这样的功能,该怎么做了?实际上录屏的原理跟录音.录像是差不多的,如果了解了我前面两篇文章中介绍 ...

  5. &lbrack;转&rsqb;C&num;实现录音录像录屏源码

    原文地址:http://www.cnblogs.com/zhuweisky/p/3593917.html 以前写过两篇录音和录像的文章(实现语音视频录制.在服务器端录制语音视频),最近有朋友问,如果要 ...

  6. WPF 截屏软件开发

    最近由于工程需要开始研发基于Windows的自动录屏软件,很多细节很多功能需要处理,毕竟一个完美的录屏软件不是你随随便便就可以写出来的.首先参考了大部分的录屏软件,在研发的过程中遇到了很多的问题:比如 ...

  7. android 调用 screenrecord 实现录屏

    首先要说明的是并未实现,本文讲一下自己的思路. adb 使用shell 命令 screenrecord 可录屏. 自己写了个app,通过Process p = Runtime.getRuntime() ...

  8. ios手机录屏软件哪个好

    苹果手机中的airplay镜像,是苹果手机系统的一大特色,可以轻松把手机屏幕投射电脑,这个功能使苹果手机相较安卓手机投屏会更加轻松,那么如何实现苹果手机投射电脑屏幕?下面小编便来分享ios手机录屏软件 ...

  9. vivo怎么录屏 手机录制屏幕详细教程

    在手机上我们经常可以刷到许多类似于手机游戏之类的屏幕视频我想肯定会有很多人好奇怎么录制的,今天小编所说的便是教大家如何在安卓手机上进行屏幕录像,下面便是关于vivo怎么录屏的具体操作方法,希望能对你们 ...

随机推荐

  1. 《java jdk7学习笔记》之java三大平台

    Sun公司在2006年底,就将三大平台正名为java SE.java EE和java ME.也有很多人习惯用J2SE.J2EE和J2ME. 1.java SE java SE(标准版)是各应用平台的基 ...

  2. Flash设置全屏后,放到网页中显示不正常

    stage.displayState = StageDisplayState.FULL_SCREEN;//全屏,注意当设置全屏后,放到网页中显示不正常

  3. Java并发包中常用类小结&lpar;一&rpar;

    从JDK1.5以后,Java为我们引入了一个并发包,用于解决实际开发中经常用到的并发问题,那我们今天就来简单看一下相关的一些常见类的使用情况. 1.ConcurrentHashMap Concurre ...

  4. SQL sum case when then else【转】

    数据库 t 表     b 表内容        Id        Name      胜负        1          张三     胜        2          李四     ...

  5. EasyUI - Messager消息框

    全局设定: JavaScript代码: //设置按钮中的文字,默认是-ok/cancel ,可以任意设置文字,比如现在的-确认/取消 $.messager.defaults = { ok: '确认', ...

  6. c&plus;&plus;多态的案例分析

    近期在研究c++中多态的应用 ,当中遇到些许的疑问与问题,可是终于的结果是不容置疑的,以下记录下我的学习过程,以纪念本个知识点. 首先,是从一个案例開始的,题目大意是这种: 设定一个多边形的公共类,然 ...

  7. 用jQuery的ajax的功能实现输入自动提示的功能

    注意事项:要使用jQuery首先要把它的包引用进来( <script type="text/javascript" language="javascript&quo ...

  8. vue的渐进式理解

    链接:https://www.zhihu.com/question/51907207/answer/136559185 渐进式代表的含义是:主张最少. 每个框架都不可避免会有自己的一些特点,从而会对使 ...

  9. webpack简介与使用

    欢迎小伙伴们为 前端导航仓库 点star https://github.com/pfan123/fr...前端导航平台访问 CommonJS 和 AMD 是用于 JavaScript 模块管理的两大规 ...

  10. 5阶m序列

    void echo32(int m) { printf("%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d\n ...